I am reading an example of React hooks. I find they write
setCount(count + 1)
But I usually wrote like this
setCount(prev => prev + 1)
Is there any difference? Which one is better?
There is a difference, in the first, the count will be based on the current value at the time that that render occurred due to the closure in the function.
The second would always use the latest value for the increment.
Because closures are a complicated topic, here's some examples. The first shows the main difference between the two.
The second example shows several ways that will allow things to work properly with closures and effects/hooks
const { useState, useEffect } = React;
function Example(){
const [count1, setCount1] = useState(0);
useEffect(()=>{
setCount1(count1 + 1);
setCount1(count1 + 1);
setCount1(count1 + 1);
},[])
const [count2, setCount2] = useState(0);
useEffect(()=>{
setCount2(prev=> prev + 1);
setCount2(prev=> prev + 1);
setCount2(prev=> prev + 1);
},[])
return <div>
Both count1 and count2 have had 3 increments.
<br/>
count1 stays at 1 because the count1 variable in the useEffect isn't change due to the closure in the arrow function in the useEffect
<br/>
Current count1: {count1}
<br/>
Current count2: {count2}
</div>
}
ReactDOM.render(<Example/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
const { useState, useEffect, useRef } = React;
function Example(){
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);
const count3Ref = useRef(count3);
count3Ref.current = count3;
useEffect(()=>{
const id = setInterval(()=>{
setCount1(count1+1);
setCount2(prev=>prev+1);
setCount3(count3Ref.current+1);
},300)
return ()=>{clearInterval(id)}
},[])
const [count4, setCount4] = useState(0);
useEffect(()=>{
const id = setTimeout(()=>{
setCount4(count4+1);
},300)
return ()=>{clearTimeout(id)}
},[count4])
return <div>
All of the counts theoretically increment every 300ms
<br/>
<br/>
count1 stays at 1 because the count1 variable in the useEffect isn't change due to the closure in the arrow function in the useEffect
<br/>
Current count1: {count1}
<hr/>
count2 uses the functional version of setCount2 so it always uses the latest version and will update properly
<br/>
Current count2: {count2}
<hr/>
count3 increments because refs are mutable by nature and allow us to bypass the closure.
<br/>
Current count3: {count3}
<hr/>
Another possiblity: count4 increments because we properly use the dependency array and force the useEffect to re-run every time count4 changes.
<br/>
Current count4: {count4}
</div>
}
ReactDOM.render(<Example/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>