React UseState - 使用先前的状态与不使用先前的状态

我想知道下面两个示例之间有什么区别。在一个示例中,我使用先前的状态,而在另一个示例中,我直接使用当前值。他们都给了我相同的结果。在哪些情况下我应该使用一种方式而不是另一种方式?提前致谢。


import React,{useState} from "react";

import ReactDOM from "react-dom";


import "./styles.css";


function App() {

  const [count, setCount] = useState(0);

  const [count2, setCount2] = useState(0);

  return (

    <div className="App">

      Count: {count}

      <button onClick={() => setCount(0)}>Reset</button>

      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>

      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>

      <br/>

      <br/>

      Count: {count2}

      <button onClick={() => setCount2(0)}>Reset</button>

      <button onClick={() => setCount2(count2 - 1)}>-</button>

      <button onClick={() => setCount2(count2 + 1)}>+</button>

    </div>

  );

}


const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);


白衣非少年
浏览 356回答 2
2回答

慕无忌1623718

对于您的示例,没有区别。但在某些情况下,这很重要。const [val, setVal] = useState(0);return (<div onClick={() => setVal(val + 1)}>&nbsp;<span onClick={() => setVal(val + 1)}>{val}</span></div>);每次点击只会将值增加 1 (0 -> 1 -> 2 -> 3)。现场示例:const {useState} = React;function Example() {&nbsp; const [val, setVal] = useState(0);&nbsp; return (<div onClick={() => setVal(val + 1)}>&nbsp; &nbsp;<span onClick={() => setVal(val + 1)}>{val}</span>&nbsp; </div>);}ReactDOM.render(<Example/>, document.getElementById("root"));<div id="root"></div><script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>const [val, setVal] = useState(0);return (<div onClick={() => setVal(oldVal => oldVal + 1)}>&nbsp;<span onClick={() => setVal(oldVal => oldVal + 1)}>{val}</span></div>);每次点击将增加 2 个值(0 -> 2 -> 4 -> 6)。现场示例:const {useState} = React;function Example() {&nbsp; const [val, setVal] = useState(0);&nbsp; return (<div onClick={() => setVal(oldVal => oldVal + 1)}>&nbsp; &nbsp;<span onClick={() => setVal(oldVal => oldVal + 1)}>{val}</span>&nbsp; </div>);}ReactDOM.render(<Example/>, document.getElementById("root"));<div id="root"></div><script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

HUWWW

因为这些对状态设置器的调用在点击处理程序中,所以保证在处理另一次点击之前重新渲染您的组件。因此,在大多数情况下,您不必使用 setter 的回调版本,您可以直接使用现有状态。(即使在并发模式下。)(请注意,如果您在多个位置处理相同的单击 [例如,一个元素及其后代],并且您希望这两个处理程序都更新值,那是另一回事- 有关示例,请参见skyboyer 的答案。)并非所有事件都是如此(例如,mousemove 没有此保证),但对于单击却是正确的。我在这个线程的推特上从 Dan Abramov 那里得到了这些信息。当时,像点击这样有这种保证的事件被称为“交互式”事件。此后,该名称已更改为“离散”事件。你可以在 React 代码的这个源文件中找到一个列表。当然,并不是所有的状态变化都直接来自事件。假设您的代码中有一个单击处理程序,它连续执行几个 ajax 调用,并且它会更新您的值以响应完成每个调用。即使您尝试非常彻底,直接更新版本也会不正确useCallback;回调版本将是正确的:const {useState, useCallback} = React;function ajaxGet() {&nbsp; &nbsp; return new Promise(resolve => setTimeout(resolve, 10));}function Example() {&nbsp; &nbsp; const [directValue, setDirectValue] = useState(0);&nbsp; &nbsp; const [callbackValue, setCallbackValue] = useState(0);&nbsp; &nbsp; const doThis = useCallback(() => {&nbsp; &nbsp; &nbsp; &nbsp; setDirectValue(directValue + 1);&nbsp; &nbsp; &nbsp; &nbsp; setCallbackValue(callbackValue => callbackValue + 1);&nbsp; &nbsp; }, [directValue, callbackValue]);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; const doThat = useCallback(() => {&nbsp; &nbsp; &nbsp; &nbsp; setDirectValue(directValue + 1);&nbsp; &nbsp; &nbsp; &nbsp; setCallbackValue(callbackValue => callbackValue + 1);&nbsp; &nbsp; }, [directValue, callbackValue]);&nbsp; &nbsp; const handleFirstFulfilled = useCallback(() => {&nbsp; &nbsp; &nbsp; &nbsp; // ...&nbsp; &nbsp; &nbsp; &nbsp; doThis();&nbsp; &nbsp; &nbsp; &nbsp; // ...&nbsp; &nbsp; &nbsp; &nbsp; return ajaxGet("something else");&nbsp; &nbsp; }, [doThis]);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; const handleSecondFulfilled = useCallback(() => {&nbsp; &nbsp; &nbsp; &nbsp; // ...&nbsp; &nbsp; &nbsp; &nbsp; doThat();&nbsp; &nbsp; &nbsp; &nbsp; // ...&nbsp; &nbsp; }, [doThat]);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; const handleClick = useCallback(() => {&nbsp; &nbsp; &nbsp; &nbsp; ajaxGet("something")&nbsp; &nbsp; &nbsp; &nbsp; .then(handleFirstFulfilled)&nbsp; &nbsp; &nbsp; &nbsp; .then(handleSecondFulfilled)&nbsp; &nbsp; &nbsp; &nbsp; .catch(error => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // ...handle/report error...&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; }, [handleFirstFulfilled, handleSecondFulfilled]);&nbsp; &nbsp; const cls = directValue !== callbackValue ? "diff" : "";&nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; &nbsp; <div className={cls}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <input type="button" onClick={handleClick} value="Click Me" />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Direct: {directValue}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Callback: {callbackValue}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; );}ReactDOM.render(<Example />, document.getElementById("root"));.diff {&nbsp; &nbsp; color: #d00;}<div id="root"></div><script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>出于这个原因,每当我设置一个基于先前值的新值时,我都会使用回调版本,除非它是专用处理click程序或类似的处理程序,在这种情况下我可以直接使用。回到事件,并发模式使非“离散”事件更容易叠加。在 cdnjs.com (v16.10.2) 上的当前版本的 React 中,我无法获得以下为 、 和 具有不同数字directValue的callbackValue内容manualValue:const {useState} = React;// Obviously this is a hack that only works when `Example` is used only once on a pagelet manualValue = 0;const manualDisplay = document.getElementById("manualDisplay");function Example() {&nbsp; &nbsp; const [directValue, setDirectValue] = useState(0);&nbsp; &nbsp; const [callbackValue, setCallbackValue] = useState(0);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; const handleMouseMove = () => {&nbsp; &nbsp; &nbsp; &nbsp; setDirectValue(directValue + 1);&nbsp; &nbsp; &nbsp; &nbsp; setCallbackValue(callbackValue => callbackValue + 1);&nbsp; &nbsp; &nbsp; &nbsp; manualDisplay.textContent = ++manualValue;&nbsp; &nbsp; };&nbsp; &nbsp; const different = directValue !== callbackValue || directValue !== manualValue;&nbsp; &nbsp; document.body.className = different ? "diff" : "";&nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; &nbsp; <div onMouseMove={handleMouseMove}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Move the mouse rapidly over this element.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Direct: {directValue}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Callback: {callbackValue}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; );}const ex = <Example />;if (ReactDOM.createRoot) {&nbsp; &nbsp; document.body.insertAdjacentHTML("beforeend", "<div>Concurrent</div>");&nbsp; &nbsp; ReactDOM.createRoot(document.getElementById("root")).render(ex);} else {&nbsp; &nbsp; ReactDOM.render(ex, document.getElementById("root"));&nbsp; &nbsp; document.body.insertAdjacentHTML("beforeend", "<div>Legacy</div>");}.diff {&nbsp; &nbsp; color: #d00;}<div id="root"></div><div>Manual: <span id="manualDisplay">0</span></div><script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>也许这只是我没有在足够多的平台上进行测试,但我无法让它们在 React 的“传统模式”中出现分歧。但是,将相同的代码与具有并发模式的实验版本一起使用,通过在其上快速摆动鼠标,很容易使directValue滞后于它,这表明事件处理程序在渲染之间运行了不止一次。callbackValuemanualValue
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript