Skip to content

Commit

Permalink
fix(edit observation): avoid unnecessary re-renders
Browse files Browse the repository at this point in the history
Ensures props maintain shallow-equality between re-renders (every keypress editing an observation field causes DraftObservationContext to re-render)
  • Loading branch information
gmaclennan committed May 3, 2019
1 parent bf55b1b commit 3a8f228
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/frontend/context/PresetsContext.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow strict
import * as React from "react";
import debug from "debug";
import memoize from "memoize-one";

import { getPresets, getFields } from "../api";
import { matchPreset } from "../lib/utils";
Expand Down Expand Up @@ -89,6 +90,7 @@ class PresetsProvider extends React.Component<Props, PresetsContext> {
getPreset: this.getPreset.bind(this),
loading: true
};
addFieldDefinitions = memoize(this.addFieldDefinitions);

async componentDidMount() {
try {
Expand Down
61 changes: 41 additions & 20 deletions src/frontend/screens/ObservationEdit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,47 @@ import SaveButton from "./SaveButton";
import DraftObservationContext from "../../context/DraftObservationContext";
import PresetsContext from "../../context/PresetsContext";

const ObservationEdit = ({ navigation }: NavigationScreenConfigProps) => (
<DraftObservationContext.Consumer>
{({ value }) => (
<PresetsContext.Consumer>
{({ getPreset }) => (
<ObservationEditView
isNew={navigation.getParam("observationId") === undefined}
onPressCategory={() => navigation.navigate("CategoryChooser")}
onPressCamera={() => navigation.navigate("AddPhoto")}
preset={getPreset(value)}
/>
)}
</PresetsContext.Consumer>
)}
</DraftObservationContext.Consumer>
);
class ObservationEdit extends React.Component<NavigationScreenConfigProps> {
static navigationOptions = ({ navigation }: any) => ({
title: navigation.getParam("observationId") ? "Edit" : "Create Observation",
headerRight: <SaveButton navigation={navigation} />
});

handleCategoryPress = () => {
this.props.navigation.navigate("CategoryChooser");
};

ObservationEdit.navigationOptions = ({ navigation }) => ({
title: navigation.getParam("observationId") ? "Edit" : "Create Observation",
headerRight: <SaveButton navigation={navigation} />
});
handleCameraPress = () => {
this.props.navigation.navigate("AddPhoto");
};

render() {
const { navigation } = this.props;
// It's important that the props are shallow-equal between renders.
// ObservationEditView is a memoized/pure component, so it will not
// re-render when props are shallow-equal. We auto-save edits to observation
// fields to DraftObservationContext on every keypress, so the children of
// the context will re-render frequently. The properties passed to
// ObservationEditView here must not be inline functions (`() =>
// doSomething)`) or inline objects (`myProp={{ foo: "bar" }}`) because
// these change on every render.
return (
<DraftObservationContext.Consumer>
{({ value }) => (
<PresetsContext.Consumer>
{({ getPreset }) => (
<ObservationEditView
isNew={navigation.getParam("observationId") === undefined}
onPressCategory={this.handleCategoryPress}
onPressCamera={this.handleCameraPress}
preset={getPreset(value)}
/>
)}
</PresetsContext.Consumer>
)}
</DraftObservationContext.Consumer>
);
}
}

export default ObservationEdit;

0 comments on commit 3a8f228

Please sign in to comment.