From f5b7b76d3086f0ffd928a409cd2f992de94386f7 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 5 Jun 2018 21:12:11 -0400 Subject: [PATCH] Updated Code Sandbox links. Mentioned Redux. --- .../2018-06-07-when-to-use-derived-state.md | 6 +- .../derived-state-anti-pattern.js | 91 ------------------- 2 files changed, 3 insertions(+), 94 deletions(-) delete mode 100644 examples/when-to-use-derived-state/derived-state-anti-pattern.js diff --git a/content/blog/2018-06-07-when-to-use-derived-state.md b/content/blog/2018-06-07-when-to-use-derived-state.md index e5b69ec4..b1eebce6 100644 --- a/content/blog/2018-06-07-when-to-use-derived-state.md +++ b/content/blog/2018-06-07-when-to-use-derived-state.md @@ -67,7 +67,7 @@ class EmailInput extends Component { } ``` -At first, this component might look okay. State is initialized to the value specified by props and updated when we type into the ``. But once our component re-renders– either because it called `setState` or because its parent re-rendered– anything we've typed into the `` will be lost! (See [this demo for an example](codesandbox://when-to-use-derived-state/derived-state-anti-pattern).) +At first, this component might look okay. State is initialized to the value specified by props and updated when we type into the ``. But once our component re-renders– either because it called `setState` or because its parent re-rendered– anything we've typed into the `` will be lost! ([See this demo for an example.](https://codesandbox.io/s/m3w9zn1z8x)) At this point, you might be wondering if this component would have worked with version 16.3. Unfortunately, the answer is "no". Before moving on, let's take a look at why this is. @@ -79,7 +79,7 @@ For the simple example above, we could "fix" the problem of unexpected re-render /> ``` -The above example binds the validation callback inline and so it will pass a new function prop every time it renders– effectively bypassing `shouldComponentUpdate` entirely. [Here is a demo](codesandbox://when-to-use-derived-state/derived-state-anti-pattern) that uses a timer to illustrate this problem. Because it might break at any time your component needs to accept new props, this design pattern is inherently fragile. +The above example binds the validation callback inline and so it will pass a new function prop every time it renders– effectively bypassing `shouldComponentUpdate` entirely. Even before `getDerivedStateFromProps` was introduced, this exact pattern led to bugs in `componentWillReceiveProps`. [Here is another demo that shows it.](https://codesandbox.io/s/jl0w6r9w59) Hopefully it's clear by now why unconditionally overriding state with props is a bad idea. But what if we were to only update the state when the email prop changes? We'll take a look at that pattern next. @@ -132,7 +132,7 @@ This approach simplifies the implementation of our component but it also has a p #### Alternative 2: Fully uncontrolled component -Another alternative would be for our component to fully own the local email state. It could still accept a prop for the initial value, but it would ignore any changes to that prop afterward. For example: +Another alternative would be for our component to fully own the "draft" email state. This might be helpful if the "committed" state lives in a state container like Redux. In that case, our component could still accept a prop for the _initial_ value, but it would ignore any changes to that prop afterward. For example: ```js class EmailInput extends Component { diff --git a/examples/when-to-use-derived-state/derived-state-anti-pattern.js b/examples/when-to-use-derived-state/derived-state-anti-pattern.js deleted file mode 100644 index ad1e099a..00000000 --- a/examples/when-to-use-derived-state/derived-state-anti-pattern.js +++ /dev/null @@ -1,91 +0,0 @@ -import React, {Fragment, PureComponent} from 'react'; -import {render} from 'react-dom'; - -// This component illustrates a getDerivedStateFromProps anti-pattern. -// Don't copy this approach! -class ExampleInput extends PureComponent { - state = { - prevProps: this.props, - text: this.props.text, - }; - - // This lifecycle will be re-run any time the component is rendered, - // Even if props.text has not changed. - // For this reason, it should not update state in the way shown below! - static getDerivedStateFromProps(props, state) { - if (props !== state.prevProps) { - // This return would override state, - // Erasing anything the user typed since the last render. - return { - prevProps: props, - text: props.text, - }; - } - return null; - } - - render() { - return ( - - ); - } - - handleChange = event => - this.setState({text: event.target.value}); -} - -// This component uses a timer to simulate arbitrary re-renders. -// In a real application, this could happen for a variety of reasons: -// Event handlers that call setState, Flux updates, network responses, etc. -class Timer extends PureComponent { - state = { - count: 0, - }; - - componentDidMount() { - this.interval = setInterval( - () => - this.setState(prevState => ({ - count: prevState.count + 1, - })), - 1000 - ); - } - - componentWillUnmount() { - clearInterval(this.interval); - } - - render() { - // Binding the validat function inline, as is done below, - // Causes a new function value to be passed each time we render. - // Even though ExampleInput is a PureComponent, - // Its shouldComponentUpdate() will always return true because of this. - // The same would be true of inline objects (e.g. styles) or arrays. - return ( - -

Type in the box below:

- -

- Each time the render count ({this.state.count}) is - updated, the text you type will be reset. This - illustrates a derived state anti-pattern. -

-
- ); - } - - exampleInstanceMethod(text) { - // ... - } -} - -render(, document.getElementById('root'));