Reactjs 无法使用useState以编程方式更改组件的值

Reactjs 无法使用useState以编程方式更改组件的值,reactjs,material-ui,Reactjs,Material Ui,我试图在使用useStatehook时以编程方式更改状态 我是这样定义的: const [data, setData] = React.useState({ text: "Hello StackOverflow!", someNumber: 2 }); 出于测试目的,我创建了一个间隔,它每秒钟增加someNumber setInterval(() => { setData({ ...data, someNumber: data.someNumber + 1 }); },

我试图在使用
useState
hook时以编程方式更改状态

我是这样定义的:

const [data, setData] = React.useState({
   text: "Hello StackOverflow!",
   someNumber: 2
});
出于测试目的,我创建了一个间隔,它每秒钟增加
someNumber

setInterval(() => {
  setData({ ...data, someNumber: data.someNumber + 1 });
}, 1000);
当我现在安装组件时,文本开始以不同的数字快速闪烁。我想间隔已经开始多次了,但我不知道为什么。这是我第一个使用新钩子的项目

可以找到我的代码的完整示例

如果链接断开,下面是组件:

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"
        />
  );
}
从“React”导入React;
从“@material ui/core/TextField”导入TextField;
导出默认函数StateTextFields(){
const[data,setData]=React.useState({
文本:“你好,StackOverflow!”,
数字:2
});
设置超时(()=>{
setData({…数据,文本:“你好吗?”});
}, 1000);
设置间隔(()=>{
setData({…data,someNumber:data.someNumber+1});
}, 1000);
返回(
);
}

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

setInterval
移动到
useffect
内部。这将阻止它在每次渲染时创建新的间隔

React.useffect(()=>{
设置间隔(()=>{
setData(prevState=>({…prevState,someNumber:prevState.someNumber+1}));
}, 1000);
设置超时(…)
},[])
这将使这些函数在安装组件时只运行一次

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


您需要使用
prevState
回调进行更新。这是因为
useffect
没有像
useffect(()=>{},[data])
making
data
那样监听对
数据所做的更改。但我们也不想改变它来听变化,因为这会导致你的间隔被无限地再次创造出来。因此,我们使用
prevState
,它始终使用状态的最新副本。

每次重新渲染时都会触发超时/间隔。您必须将它放入一个
useffect
中,它取代了生命周期挂钩,如
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"

这里有很多问题

  • 您正在设置处理相同数据的多个超时/间隔。由于异步性,它们在不同的时间段更新相同的值。例如:
    超时
  • 几乎与
    间隔
    同时更改
    数据
    。这导致超时计算
    2+1
    ,同时间隔考虑
    数据。一些数字仍然是
    2
    ,而不是
    3
  • 当不使用像
    React这样的钩子时,useEffect
    React将在每次重新启动组件时重新创建方法,即每次状态更改时重新创建方法,即每次
    数据更改时重新创建方法
  • 正如所指出的,以下解决方案解决了这些问题。我添加了一些注释来解释原因

    React.useffect(()=>{
    设置间隔(()=>{
    //在这里使用prevState很重要,因为它不需要“数据”作为
    //这将迫使您在每次“数据”更改时重新创建钩子
    setData(prevState=>({…prevState,someNumber:prevState.someNumber+1}));
    }, 1000);
    设置超时(…)
    },[])
    
    刚刚在您的沙箱上测试过。当将更新功能更改为如我的回答所示时,它将按预期工作。谢谢!成功了。伟大的我更新了我的答案,解释了为什么我们必须这样做。