Browse Source

Added example of updating subscription from props

The nuance of this particular example came up while updating react-relay
main
Brian Vaughn 7 years ago
parent
commit
442591cec9
  1. 12
      content/blog/2018-02-07-update-on-async-rendering.md
  2. 6
      examples/update-on-async-rendering/adding-event-listeners-after.js
  3. 6
      examples/update-on-async-rendering/adding-event-listeners-before.js
  4. 46
      examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js

12
content/blog/2018-02-07-update-on-async-rendering.md

@ -105,8 +105,16 @@ For this reason, the recommended way to add listeners/subscriptions is to use th
>
> Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this.
You may also need to update subscriptions based on changes in `props`. In this case, you should also wait to _remove_ a subscription until `componentDidUpdate`. For example:
`embed:update-on-async-rendering/adding-event-listeners-after-continued.js`
### Updating subscriptions when `props` change
(This is a continuation of [the example above](#adding-event-listeners-or-subscriptions).)
You may also need to update subscriptions based on changes in `props`. In this case, you should also wait until `componentDidUpdate` before _removing_ a subscription. In the event that a render is cancelled before being committed, this will prevent us from unsubscribing prematurely.
Howeveer, waiting to unsubscribe means that we will need to be careful about how we handle events that are dispatched in between `getDerivedStateFromProps` and `componentDidUpdate` so that we don't put stale values into the `state`. To do this, we should use the callback form of `setState` and compare the event dispatcher to the one currently in `state`.
Here is a example:
`embed:update-on-async-rendering/updating-subscriptions-when-props-change-after.js`
### Updating `state` based on `props`

6
examples/update-on-async-rendering/adding-event-listeners-after.js

@ -31,7 +31,9 @@ class ExampleComponent extends React.Component {
);
}
handleSubscriptionChange = subscribedValue => {
this.setState({subscribedValue});
handleSubscriptionChange = dataSource => {
this.setState({
subscribedValue: dataSource.value,
});
};
}

6
examples/update-on-async-rendering/adding-event-listeners-before.js

@ -18,7 +18,9 @@ class ExampleComponent extends React.Component {
);
}
handleSubscriptionChange = subscribedValue => {
this.setState({subscribedValue});
handleSubscriptionChange = dataSource => {
this.setState({
subscribedValue: dataSource.value,
});
};
}

46
examples/update-on-async-rendering/adding-event-listeners-after-continued.js → examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js

@ -1,17 +1,16 @@
// After
class ExampleComponent extends React.Component {
// highlight-range{1-3}
// highlight-range{1-4}
state = {
dataSource: this.props.dataSource,
subscribedValue: this.props.dataSource.value,
};
// highlight-line
// highlight-range{1-10}
// highlight-range{1-8}
static getDerivedStateFromProps(nextProps, prevState) {
if (
prevState.subscribedValue !==
nextProps.dataSource.value
) {
if (nextProps.dataSource !== prevState.dataSource) {
return {
dataSource: nextProps.dataSource,
subscribedValue: nextProps.dataSource.value,
};
}
@ -24,10 +23,10 @@ class ExampleComponent extends React.Component {
// highlight-line
// highlight-range{1-11}
componentDidUpdate(prevProps, prevState) {
if (this.props.dataSource !== prevProps.dataSource) {
if (this.state.dataSource !== prevState.dataSource) {
// Similar to adding subscriptions,
// It's only safe to unsubscribe during the commit phase.
prevProps.dataSource.unsubscribe(
prevState.dataSource.unsubscribe(
this.handleSubscriptionChange
);
@ -36,32 +35,39 @@ class ExampleComponent extends React.Component {
}
componentWillUnmount() {
this.props.dataSource.unsubscribe(
this.state.dataSource.unsubscribe(
this.handleSubscriptionChange
);
}
// highlight-range{1-18}
// highlight-range{1-14}
finalizeSubscription() {
// Event listeners are only safe to add during the commit phase,
// So they won't leak if render is interrupted or errors.
this.props.dataSource.subscribe(
this.state.dataSource.subscribe(
this.handleSubscriptionChange
);
// External values could change between render and mount,
// In some cases it may be important to handle this case.
if (
this.state.subscribedValue !==
this.props.dataSource.value
) {
this.setState({
subscribedValue: this.props.dataSource.value,
});
const subscribedValue = this.state.dataSource.value;
if (subscribedValue !== this.state.subscribedValue) {
this.setState({subscribedValue});
}
}
// highlight-line
// highlight-range{1-13}
handleSubscriptionChange = dataSource => {
this.setState(state => {
// If this event belongs to the current data source, update.
// Otherwise we should ignore it.
if (dataSource === state.dataSource) {
return {
subscribedValue: dataSource.value,
};
}
handleSubscriptionChange = subscribedValue => {
this.setState({subscribedValue});
return null;
});
};
}
Loading…
Cancel
Save