Home  >  Q&A  >  body text

Why do you need to use React useState in the form of functional updates?

I was reading the React Hook documentation on feature updates and looked at the following quote:

The " " and "-" buttons use functional form because of the update Value is based on previous value

But I don't see what is the purpose of requiring feature updates, and what's the difference between them and directly using the old state to calculate the new state.

Why does React useState Hook's updater function require a functional update form? Are there any examples where we can clearly see the difference (and therefore using direct update would cause an error)?

For example, if I change this example from the 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>
    </>
  );
}

Update directlycount:

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 don't see any difference in behavior and can't imagine a situation where the count won't be updated (or isn't up to date). Because every time the count changes, a new closure of onClick is called, capturing the latest count.

P粉155551728P粉155551728385 days ago695

reply all(2)I'll reply

  • P粉818125805

    P粉8181258052023-10-23 10:02:44

    I recently stumbled upon this need. For example, let's say you have a component that fills an array with a certain number of elements and is able to append to that array based on some user action (like in my case I load a feed 10 items at a time because the user keeps Scroll down the screen. The code looks a little 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 dependencies list in the useEffect hook because you're calling setFeedItems in it, so you'll be stuck in a loop.

    Feature updates to the rescue:

    useEffect(() => {
        if (data) {
          setFeedItems(prevItems => [...prevItems, ...data.items]);
        }
      }, [data]);     //  <--- all good now

    reply
    0
  • P粉457445858

    P粉4574458582023-10-23 09:44:55

    State updates in React are asynchronous. Therefore, when you next update, there may be old values ​​in count. For example, compare the results of these two code examples:

    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>
        </>
      );
    }

    reply
    0
  • Cancelreply