Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why React useState with functional update form is needed?

I'm reading React Hook documentation about functional updates and see this quote:

The ”+” and ”-” buttons use the functional form, because the updated value is based on the previous value

But I can't see for what purposes functional updates are required and what's the difference between them and directly using old state in computing new state.

Why functional update form is needed at all for updater functions of React useState Hook? What are examples where we can clearly see a difference (so using direct update will lead to bugs)?

For example, if I change this example from documentation

function Counter({initialCount}) {   const [count, setCount] = useState(initialCount);   return (     <>       Count: {count}       <button onClick={() => setCount(initialCount)}>Reset</button>       <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>       <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>     </>   ); } 

to updating count directly:

function Counter({initialCount}) {   const [count, setCount] = useState(initialCount);   return (     <>       Count: {count}       <button onClick={() => setCount(initialCount)}>Reset</button>       <button onClick={() => setCount(count + 1)}>+</button>       <button onClick={() => setCount(count - 1)}>-</button>     </>   ); } 

I can't see any difference in behaviour and can't imagine case when count will not be updated (or will not be the most recent). Because whenever count is changing, new closure for onClick will be called, capturing the most recent count.

like image 225
likern Avatar asked Sep 06 '19 20:09

likern


People also ask

What is the purpose of useState in React?

The React useState Hook allows us to track state in a function component. State generally refers to data or properties that need to be tracking in an application.

What is functional update in React?

As mentioned in the beginning, it can accept a function instead of a value. This function will be called by React and the current version of the state will be passed as an argument. This way is called a “functional update.”

What is the purpose of useState when and why will you use it?

useState is a Hook (function) that allows you to have state variables in functional components. You pass the initial state to this function and it returns a variable with the current state value (not necessarily the initial state) and another function to update this value.

Why React setState useState does not update immediately?

The answer: They're just queues setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.


2 Answers

State update is asynchronous in React. So it is possible that there would be old value in count when you're updating it next time. Compare, for example, result of these two code samples:

function Counter({initialCount}) {   const [count, setCount] = useState(initialCount);   return (     <>       Count: {count}       <button onClick={() => setCount(initialCount)}>Reset</button>       <button onClick={() => {         setCount(prevCount => prevCount + 1);          setCount(prevCount => prevCount + 1)}       }>+</button>     </>   ); } 

and

function Counter({initialCount}) {   const [count, setCount] = useState(initialCount);   return (     <>       Count: {count}       <button onClick={() => setCount(initialCount)}>Reset</button>       <button onClick={() => {         setCount(count + 1);          setCount(count + 1)}       }>+</button>     </>   ); } 
like image 98
Alex Gessen Avatar answered Sep 30 '22 15:09

Alex Gessen


I stumbled into a need for this recently. For example let's say you have a component that fills up an array with some amount of elements and is able to append to that array depending on some user action (like in my case, I was loading a feed 10 items at a time as the user kept scrolling down the screen. the code looked kind of like this:

function Stream() {   const [feedItems, setFeedItems] = useState([]);   const { fetching, error, data, run } = useQuery(SOME_QUERY, vars);    useEffect(() => {     if (data) {       setFeedItems([...feedItems, ...data.items]);     }   }, [data]);     // <---- this breaks the rules of hooks, missing feedItems  ... <button onClick={()=>run()}>get more</button> ...  

Obviously, you can't just add feedItems to the dependency list in the useEffect hook because you're invoking setFeedItems in it, so you'd get in a loop.

functional update to the rescue:

useEffect(() => {     if (data) {       setFeedItems(prevItems => [...prevItems, ...data.items]);     }   }, [data]);     //  <--- all good now 
like image 27
G Gallegos Avatar answered Sep 30 '22 15:09

G Gallegos