Home  >  Q&A  >  body text

Why is useState and functional update forms in React necessary?

<p>I was reading the documentation about React Hook function updates and saw this sentence: </p> <blockquote> <p>The " " and "-" buttons use functional form because the updated value is based on the previous value</p> </blockquote> <p>But I can't see what the purpose of function updates is, and the difference between them and using the old state directly when calculating the new state. </p> <p><strong>Why does the update function of React useState Hook require a function update form? </strong> <strong>What are some examples where the difference is clearly visible (and therefore using direct update would result in an error)? </strong></p> <p>For example, if I change the example in the documentation to: </p> <pre class="brush:php;toolbar:false;">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> </> ); }</pre> <p>Update <code>count</code> directly: </p> <pre class="brush:php;toolbar:false;">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> </> ); }</pre> <p>I don't see any difference in behavior, and can't imagine a situation where count won't be updated (or isn't up to date). Because every time the count changes, a new closure will be called to capture the latest count. </p>
P粉041856955P粉041856955445 days ago486

reply all(2)I'll reply

  • P粉986937457

    P粉9869374572023-08-25 13:15:08

    I encountered this need recently. For example, let's say you have a component that populates an array and is able to load a dynamic source of 10 items each time based on some action by the user (like in my case when the user keeps scrolling down the screen .The code looks a bit 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]);     // <---- 这违反了hooks的规则,缺少了feedItems
    
    ...
    <button onClick={()=>run()}>获取更多</button>
    ...

    Obviously, you can't simply add feedItems to the dependency list of the useEffect hook, because you call setFeedItems in it, so you'll get stuck in a loop.

    Functional updates to the rescue:

    useEffect(() => {
        if (data) {
          setFeedItems(prevItems => [...prevItems, ...data.items]);
        }
      }, [data]);     //  <--- 现在一切都好了

    reply
    0
  • P粉238355860

    P粉2383558602023-08-25 00:00:10

    In React, status updates are asynchronous. Therefore, on the next update, there may be old values ​​in count. For example, compare the results of the following two code examples:

    function Counter({initialCount}) {
      const [count, setCount] = useState(initialCount);
      return (
        <>
          Count: {count}
          <button onClick={() => setCount(initialCount)}>重置</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)}>重置</button>
          <button onClick={() => {
            setCount(count + 1); 
            setCount(count + 1)}
          }>+</button>
        </>
      );
    }

    reply
    0
  • Cancelreply