【问题标题】:Don't understand how to update React hook不明白如何更新 React 钩子
【发布时间】:2020-05-25 00:58:25
【问题描述】:

我已经尝试过数十次问这个对我来说难以理解的问题。我已经做了几乎最简单的例子来问这个问题。

我在handleChange 方法中更改了钩子的值。但是console.log 总是显示以前的值,而不是新值。这是为什么呢?

我需要更改钩子的值,然后用它来代替console.log 做其他事情。但我不能,因为钩子总是没有我刚刚尝试放入的内容。

const options = ["Option 1", "Option 2"];

export default function ControllableStates() {
  const [value, setValue] = React.useState(options[0]);

  const handleChange = val => {
    setValue(val);
    console.log(value);
  };

  return (
    <div>
      <div>{value}</div>
      <br />
      <Autocomplete
        value={value}
        onChange={(event, newValue) => {
          handleChange(newValue);
        }}
        options={options}
        renderInput={params => (
          <TextField {...params} label="Controllable" variant="outlined" />
        )}
      />
    </div>
  );
}

你可以在这里试试。 https://codesandbox.io/s/awesome-lumiere-y2dww?file=/src/App.js

【问题讨论】:

  • 当您调用setValue(val) 时,您在内部设置了新值,但是变量value 不再更改,它总是在调用useState 时保存最新值的副本。此时您可以将value 视为一个不可变变量,它永远不会改变。将console.log 放在处理程序之外,您会看到每次执行组件函数时,值都会在 handleCgange 被调用之后发生变化。
  • 我需要更改钩子的值,然后用它来代替 console.log 做其他事情。那我该如何实现呢?如何立即使用组件的新值?
  • 我认为您正在理解钩子和组件如何工作错误。每次执行组件函数时,它只是最新状态的“快照”。当 a) 输入更改或 b) 状态更改时,React 将呈现组件。调用 setter 函数进行状态更改。但是,您只能在组件函数的下一次调用中看到新状态。你实际上做得对,只需调用setValue,React 就会再次渲染你的组件,此时你会看到变化。可以看到&lt;div&gt;{value}&lt;/div&gt;后面的值是正确的...
  • 你设置了其他东西,所以这表明它工作正常。意识到value变量是const,它是不可变的,一旦设置就不能改变。
  • 好的,我明白了,但我不明白如何使用用户输入。在该组件中选择某些内容后,用户按下按钮,按钮需要使用当前选择的值

标签: javascript reactjs react-hooks


【解决方案1】:

我认为问题在于您将值记录在 handleChange 函数中。控制台记录函数外部的值会记录正确的值。链接:https://codesandbox.io/s/async-fast-6y71b

【讨论】:

  • 这不是答案
【解决方案2】:

Hooks 不会立即更新您想要更新的值,正如您可能对类所期望的那样(尽管这也不能保证)

状态钩子,当调用setValue 时会触发重新渲染。在该新渲染中,状态将具有您预期的新值。这就是为什么您的 console.log 看到旧值的原因。

把它想象成在每个渲染中,状态值只是该组件函数调用的局部变量。并认为您的渲染结果是您的状态+该渲染调用中的道具的结果。每当这两个更改中的任何一个(来自您的父组件的道具;来自您的 setXXX 函数的状态)时,都会触发一个新的渲染。

如果您移出回调处理程序的console.log 外部(即在您渲染的主体中),您将在交互后发生的渲染中看到该状态已正确记录。

从这个意义上说,在您的交互回调事件中,您只需要担心正确更新您的状态,并且下一次渲染会在给定新道具/状态的情况下重新渲染结果

【讨论】:

  • 我需要更改钩子的值,然后用它来代替 console.log 做其他事情。那我该如何实现呢?如何立即使用组件的新值?
【解决方案3】:

该值不会同步“更改” - 它甚至使用const 声明,因此即使在同一范围内更改它的概念也没有意义。

当使用钩子改变状态时,新值会在组件被重新渲染时看到。因此,要使用“新值”记录和执行操作,请在函数主体中对其进行检查:

const ControllableStates = () => {
  const [value, setValue] = React.useState(options[0]);

  const handleChange = val => {
    setValue(val);
  };
  // ADD LOG HERE:
  console.log('New or updated value:', value);

  return (
    <div>
      <div>{value}</div>
      <br />
      <Autocomplete
        value={value}
        onChange={(event, newValue) => {
          handleChange(newValue);
        }}
        options={options}
        renderInput={params => (
          <TextField {...params} label="Controllable" variant="outlined" />
        )}
      />
    </div>
  );
}

【讨论】:

  • 我需要更改钩子的值,然后用它来代替 console.log 做其他事情。那我该如何实现呢?如何立即使用组件的新值?
  • 组件不会立即重新渲染 - 它是异步的。用真正的实现替换 console.log 会导致问题,你到底需要做什么?
  • 一旦我改变了自动完成的状态,我必须使用它的新状态来发送请求
【解决方案4】:

您在handleChange 中打印出旧的value,而不是新的val。 即

  const handleChange = val => {
    setValue(val);
    console.log(value);
  };

应该是:

  const handleChange = val => {
    setValue(val);
    console.log(val);
  };

【讨论】:

    【解决方案5】:

    实际上,让我们回过头来看看这个场景背后的逻辑。 你应该只使用“handleChange”函数来更新状态钩子,让其他的逻辑依赖于状态钩子值,这主要是使用“useEffect”钩子来完成的。

    您可以将代码重构为如下所示:

      const handleChange = val => {
        setValue(val);
       };
    
      React.useEffect(() => {
        console.log(value);
        // do your logic here
      }, [value])
    

    【讨论】:

    • 好的,假设我有两个不同的自动完成组件使用两个不同的钩子。我可以创建两个 useEffect 挂钩吗?
    • 好的,这有点帮助,但我还是有问题。在自动完成组件中选择某些内容后,我需要按下一个按钮,该按钮必须使用选定的值。不明白该怎么做
    • 查看此代码框示例,我对其进行了更新,codesandbox.io/s/… 如果它回答了您的问题,请接受此答案
    【解决方案6】:

    所以我认为主要问题是您不了解 React 处理组件和状态。

    所以,我将大大简化 React 的功能。

    • React 渲染一个新组件并记住它的状态,它的输入(又名 props),它是孩子的​​状态和输入。
    • 如果在任何给定点输入更改或状态更改,React 将呈现 再次调用组件函数。

    考虑一下:

    function SomeComponent(text) {
        return (<div>The <i>text</i> prop has the value {text}</div>)
    }
    

    假设初始prop值是"abc",React会调用SomeComponent("abc"),然后函数返回 &lt;div&gt;The &lt;i&gt;text&lt;/i&gt; prop has the value abc&lt;/div&gt; 和 React 会渲染它。 如果 prop text 没有改变,那么 React 将不再做任何事情。

    现在父组件将prop改为"def",现在React会调用 SomeComponent("def") 它会返回 &lt;div&gt;The &lt;i&gt;text&lt;/i&gt; prop has the value def&lt;/div&gt;,这不同于 最后一次调用,因此 React 将更新 DOM 以反映更改。

    现在让我们介绍一下状态

    function SomeComponent() {
        const [name, setName] = React.useState("John")
    
        function doSomething()
        {
            alert("The name is " + name)
        }
    
        return (
            <p>Current name: {name}</p>
            <button onClick={() => setName("Mary")}>Set name to Mary</button>
            <button onClick={() => setName("James")}>Set name to James</button>
            <button onClick={() => doSomething()}>Show current name</button>
        )
    }
    

    所以这里 React 将调用 SomeComponent() 并呈现名称 John 和 3 按钮。请注意,name 变量的值在 当前执行,因为它被声明为const。这个变量只反映 状态的最新值。

    当你按下第一个按钮时,setName() 被执行。 React 将在内部存储 状态的新值,并且由于状态的变化,它将呈现 再次调用该组件,因此将再次调用SomeComponent()。现在变量name 将 再次反映状态的最新值(这就是useState所做的),所以在这种情况下Mary。反应 将意识到必须更新 DOM 并打印名称 Mary

    如果你按下第三个按钮,它将调用doSomething(),它将打印 name 变量的最新值,因为每次 React 调用 SomeComponent()doSomething() 函数再次使用最新创建 name 的值。所以一旦你打电话给setName(),你就不需要做 任何特殊的东西来获得新的价值。 React 将负责调用 再次组件函数。

    所以当你不使用类组件而是使用函数组件时,你必须思考 不同的是:该函数一直被 React 调用,并且在任何一个 执行它反映了该特定时间点的最新状态。所以当你 调用useState钩子的setter,你知道组件函数会 再次调用,useState 将返回新值。

    我建议您阅读 this article,同时从 React 文档中再次阅读 Components and Props

    那么你应该怎么做呢?好吧,像这样:

    const options = ["Option 1", "Option 2"];
    
    export default function ControllableStates() {
      const [value, setValue] = React.useState(options[0]);
    
      const handleChange = val => {
        setValue(val);
        console.log(value);
      };
    
      const handleClick = () => {
          // DOING SOMETHING WITH value
          alter(`Now I'm going to do send ${value}`);
      }
    
      return (
        <div>
          <div>{value}</div>
          <br />
          <Autocomplete
            value={value}
            onChange={(event, newValue) => {
              handleChange(newValue);
            }}
            options={options}
            renderInput={params => (
              <TextField {...params} label="Controllable" variant="outlined" />
            )}
          />
          <button type="button" onClick={handleClick}>Send selected option</button>
        </div>
      );
    }
    

    CodeSandbox

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-29
      • 1970-01-01
      • 2020-06-01
      • 2020-01-12
      • 1970-01-01
      • 1970-01-01
      • 2021-05-23
      • 2020-11-02
      相关资源
      最近更新 更多