【问题标题】:Unable to change value of component programmatically using useState无法使用 useState 以编程方式更改组件的值
【发布时间】:2019-11-14 14:55:46
【问题描述】:

我正在尝试在使用 useState 挂钩时以编程方式更改状态。

我是这样定义的:

const [data, setData] = React.useState({
   text: "Hello StackOverflow!",
   someNumber: 2
});

出于测试目的,我创建了一个每秒增加someNumber 的间隔。

setInterval(() => {
  setData({ ...data, someNumber: data.someNumber + 1 });
}, 1000);

当我现在安装组件时,文本开始以不同的数字快速闪烁。我猜 Interval 已多次启动,但我不知道为什么。这是我第一个使用新钩子的项目。

我的代码的完整示例可以找到on code sandbox snippet

如果链接断开,这里是组件:

import React from "react";
import TextField from "@material-ui/core/TextField";

export default function StateTextFields() {

  const [data, setData] = React.useState({
    text: "Hello StackOverflow!",
    someNumber: 2
  });

  setTimeout(() => {
    setData({ ...data, text: "How are you?" });
  }, 1000);

  setInterval(() => {
    setData({ ...data, someNumber: data.someNumber + 1 });
  }, 1000);

  return (
        <TextField
          id="standard-name"
          label="Name"
          value={data.text + " " + data.someNumber}
          margin="normal"
        />
  );
}

【问题讨论】:

  • 刚刚在您的沙箱上进行了测试。将更新功能更改为我的答案中所示时,它按预期工作

标签: reactjs material-ui


【解决方案1】:

将您的setInterval 移动到useEffect 中。这将阻止它在每次渲染时创建新的间隔。

React.useEffect(() => {
  setInterval(() => {
      setData(prevState => ({ ...prevState, someNumber: prevState.someNumber + 1 }));
    }, 1000);
  setTimeout(...)
},[])

这将使这些函数只在组件挂载时运行一次。

无条件调用修改函数组件内部状态的函数就像在类组件的render 中执行相同操作一样。它将修改状态,这会导致重新渲染,这将无限地修改状态,等等。

您需要使用prevState 回调来更新。这是因为useEffect 没有听取对data 所做的更改,例如useEffect(() =&gt; {}, [data]) 使data 过时。但是我们也不想改变它来监听变化,因为那会导致你的间隔再次被无限地创建。所以我们使用prevState,它总是使用最新的状态副本。

【讨论】:

【解决方案2】:

您正在为每个组件渲染调用设置setTimeoutsetInterval

【讨论】:

    【解决方案3】:

    您的超时/间隔将在每次重新渲染时触发。你必须把它放在一个useEffect 中,它取代了像componentDidMount() 这样的生命周期钩子。

    例子:

    useEffect(()=>{
    
        const myInterval = setInterval(() => {
          setData({ ...data, someNumber: data.someNumber + 1 })
        }, 1000);
    
        return ()=>{myInterval.clearInterval()} //cleanup
    
    }, []) //The empty array means "only do this once, after the first render"
    

    【讨论】:

      【解决方案4】:

      这里有多个问题。

      1. 您正在设置处理相同数据的多个超时/间隔。由于异步性,它们在不同的时间段内更新相同的值。例如:
        timeoutinterval 几乎同时更改data。这导致超时计算2 + 1,同时区间认为data.someNumber仍然是2而不是3
      2. 当不使用像 React.useEffect 这样的钩子时,react 将在每次重新渲染组件时重新创建方法,也就是每次状态更改时,也就是每次 data 更改时。

      正如BrianThompson 指出的,以下解决方案解决了这些问题。我添加了一些 cmets 来解释原因。

      React.useEffect(() => {
        setInterval(() => {
            // using prevState is essential here because it negates the need for 'data' as
            // that would force you to recreate the hook every time 'data' changes
            setData(prevState => ({ ...prevState, someNumber: prevState.someNumber + 1 }));
          }, 1000);
        setTimeout(...)
      },[])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-09
        • 1970-01-01
        • 2012-03-30
        • 2011-07-25
        • 1970-01-01
        相关资源
        最近更新 更多