Initialize the
const [state, setState] = useState()
Copy the code
React guarantees that the setState function is immutable during rerendering, so it can be omitted from the useEffect or userCallback dependencies list
Lazy initialization
function Table(props) {
// ⚠️ createRows() is called on every render
const [rows, setRows] = useState(createRows(props.count));
// ...
}
Copy the code
function Table(props) {
// ✅ createRows() is only called once
const [rows, setRows] = useState(() = > createRows(props.count));
// ...
}
Copy the code
So let’s look at the difference between these two, the first one is function execution, which is equivalent to
const value = createRows(props.count);
const [rows, setRows] = useState(value);
Copy the code
So it is executed every time on re-render, but the value of rows is unchanged unless setRows is called; The second is the function definition, which is executed only once during initialization.
The second is lazy initialization, which avoids expensive computations repeated.
update
UseState doesn’t have a callback function like this.setState, so you can do something after making sure the state changes. But you can do that with useEffect.
const [value, setValue] = useState(0)
function () {
setValue(newValue)
}
useEffect(() = > {
// ...
}, [value])
Copy the code
If the update returns exactly the same value as the current state, subsequent rerendering will be skipped entirely. Use object.is to compare values.
Is setState asynchronous or synchronous?
Strictly speaking, neither asynchronous nor synchronous. SetState is sometimes not updated in real time because React is optimized to process updates in batches in the event handler. At some point, the setState is updated synchronously. Let’s look at the two cases separately.
batch
In the event handler, React processes updates in batches, such as
const onClick = () = > {
this.setState({a: 1})
this.setState({a: 2})}Copy the code
Components are only updated once
Updates in asynchronous code (promise, async/await, setTimeout/setInterval, fetch) are not processed in batches. For example:
const onClick = () = > {
callAPI().then(() = > {
this.setState({a: 1})
this.setState({a: 2})})}Copy the code
When a component is updated twice, we call it outside of the React event handlers. When an update occurs outside of the React event handler, the callback occurs after the React execution is complete. React cannot be updated in batches.
So why is setState designed to be “asynchronous,” or batch?
The simple summary is to avoid multiple updates. If setState is synchronous, react will rerender multiple times when setState is called multiple times, some of which is unnecessary.
?? When setState is called multiple times, react is rerendered multiple times. Doesn’t it affect the execution of the logic after setState?
Don’t. When React calls setState asynchronously in the class component, it will first update the state, re-render, and then execute it in order according to the lifecycle. After the lifecycle is complete, it will continue to execute the logic after setState. However, in functional components, react executes the logic after setState and re-render (until another setState is encountered) and then executes the lifecycle. The differences are as follows: Class-based components
state = { a: 0.b: 0 } componentDidMount() { setTimeout(() = > { this.setState({a: 1}) console.log('a'.this.state.a) this.setState({b: 1}) console.log('b'.this.state.b) }, 1000)}componentDidUpdate() { console.log('updated'.this.state.a, this.state.b) } render() { const {a, b} = this.state return <div>{a} {b}</div> } Copy the code
Output: updated 1 0, a 1, updated 1 1, B 1 Functional component
const [a, setA] = useState(0) const [b, setB] = useState(0) useEffect(() = > { setTimeout(() = > { setA(1) console.log('a') setB(1) console.log('b')},1000) }, []) useEffect(() = > { console.log('effect', a, b) }, [a, b]) return <div>{a} {b}</div> Copy the code
Output: effect 0 0, a, effect 1 0, b, effect 1 1
So how do you optimize for updates that can’t be batch updated?
- Integrate state into an object so that multiple updates become a single update
- You can manually force batch updates using an API provided by React.
promise.then(() = > {
ReactDom.unstable_batchedUpdates(() = > {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
})
// When we exit unstable_batchedUpdates, re-renders once
})
Copy the code
React Event Handlers are included in unstable_batchedUpdates by default, so they can be updated in batches. Elsewhere you can force unstable_batchedUpdates, and when unstable_batchedUpdates ends, updates are refreshed to the interface. 3. useReducer