Derived state of React
Prior to react 16.3, the only way to update the state s inside a component when props changed was to use componentWillReceiveProps followed by a getDerivedStateFromProps.
There are two scenarios when using a derived state:
- Manage a copy of the props passed by the parent component in the state of the child component
- Receive props changes in componentWillReceiveProps to update subcomponent state
Copy props to state
Most people would think that componentWillReceiveProps and getDerivedStateFromProps would only be called if the props changed, but not. When the parent component is re-rendered, these two life cycles are re-invoked, resulting in the loss of state in the child component.
class DefaultInput extends Component { constructor(props) { super(props); this.state = { value: props.value, }; } handleChange = (event) => { this.setState({ value: event.target.value }); }; componentWillReceiveProps(nextProps) { this.setState({ value: nextProps.value }); } render() { return <input value={this.state.value} onChange={this.handleChange} />; } } class State extends Component { constructor(props) { super(props); this.state = { count: 0, }; } componentDidMount() { this.interval = setInterval( () => this.setState((prevState) => ({ count: prevState.count + 1, })), 5000, ); } componentWillUnmount() { clearInterval(this.interval); } render() { return <DefaultInput value="My name is Xiao Ming" />; } }
Reflections on Problems
- So what if you just want a default? It is possible not to do props updates in subcomponents that change the state.
- Another improvement is that instead of updating the state in the subcomponents, we just listen on the props and update the state in the lifecycle of the subcomponents as the props change. This situation is considered unnecessary by the individual. If the state is updated through the props change of the parent component and not in the child component, then there is no need to maintain this state at all, and the component is maintained directly as a controlled component (which is one of the best practices). This can cause component reuse problems, for example, if I have a tabs to switch between two forms, the default values of the input boxes of the forms are the same, passed in through props, but props are not changed, which can cause component reuse, typical password boxes are mixed up, which is not expected to be seen by users.
Best Practices
The key to the above problem is to ensure that there is only one data source for any data and to avoid copying it directly
Fully Controlled Components
This is to remove the state from the child components and change all state changes through the parent component to change props to be passed, which is also consistent with the idea of one-way data flow.
function DefaultInput(props) { return <input onChange={props.onChange} value={props.value} />; }
Completely uncontrolled components
A child component maintains a state internally after accepting a value passed from the parent component, and this state change has nothing to do with the parent component, which also guarantees the purity of the data source. But there is also a problem that react reuses existing components to save money.
class DefaultInput extends Component { state = { value: this.props.value }; handleChange = event => { this.setState({ value: event.target.value }); }; render() { return <input onChange={this.handleChange} value={this.state.value} />; } } class State extends Component { constructor(props) { super(props); this.state = { show: true, }; } handleClick = () => { console.log(this.state.show); this.setState({ show: !this.state.show, }); }; render() { return ( <div> {this.state.show ? ( <DefaultInput value="My name is Xiao Ming" key="1" /> ) : ( <DefaultInput value="My name is Xiao Fang" key="2" /> )} <button onClick={this.handleClick}>switch</button> </div> ); } }
The above code achieves the function of switching input components by button. When the user clicks the button, he or she will switch the input box with default value Xiao Ming or Xiao Fang. However, if the key value is not given, you can switch between Xiao Ming and Xiao Ming no matter how, which involves the component update strategy of react. We need to manually tell react that these are two different components. You need to destroy the original component and recreate it when switching. This does not incur much overhead. It is faster to do so than when updating logic within the component is very complex, because the diff cost of the component is omitted.
Add key value invalidation problem
Sometimes it's not useful to add a key value to a component because it's too expensive to create a component. When I change some fields, I see if some special properties change. For example, when I use this input box component, I change the value only by value, but I can give each component a unique id. If the component's id changes, I reset the state value (in this case, value) inside the component.
componentWillReceiveProps = (nextProps) => { if (nextProps.id !== this.state.id) { this.setState({ value: nextProps.value, id: nextProps.id, }); } };
Use ref to reset components
This method is not ideal relative to other methods, which results in secondary rendering of the component, defining a method within the subcomponent to reset the state value inside the component, and then switching the subcomponent to obtain this method by ref.