mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-24 04:33:10 +00:00
Updated Code Sandbox links. Mentioned Redux.
This commit is contained in:
@@ -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 `<input>`. But once our component re-renders– either because it called `setState` or because its parent re-rendered– anything we've typed into the `<input>` 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 `<input>`. But once our component re-renders– either because it called `setState` or because its parent re-rendered– anything we've typed into the `<input>` 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 {
|
||||
|
||||
@@ -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 (
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
value={this.state.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Fragment>
|
||||
<p>Type in the box below:</p>
|
||||
<ExampleInput
|
||||
exampleFunctionProp={this.exampleInstanceMethod.bind(
|
||||
this
|
||||
)}
|
||||
text="example@google.com"
|
||||
/>
|
||||
<p>
|
||||
Each time the render count ({this.state.count}) is
|
||||
updated, the text you type will be reset. This
|
||||
illustrates a derived state anti-pattern.
|
||||
</p>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
exampleInstanceMethod(text) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
render(<Timer />, document.getElementById('root'));
|
||||
Reference in New Issue
Block a user