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

时间:2019-11-14 14:55:46

标签: reactjs material-ui

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

我是这样定义的:

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

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

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

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

可以找到我的代码的完整示例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"
        />
  );
}

4 个答案:

答案 0 :(得分:2)

setInterval移到useEffect内。这将阻止它为每个渲染创建新的间隔。

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

这将使这些功能仅在组件安装时运行一次。

无条件调用一个函数来修改功能组件内部的状态,就像在类组件的render中进行操作一样。它将修改状态,从而导致重新渲染,这将无限地修改状态,等等。

您需要使用prevState回调进行更新。这是因为useEffect没有像data那样使useEffect(() => {}, [data])过时,而没有听对data的更改。但是我们也不想更改它以听取更改,因为那样会使您的间隔再次无限创建。因此,我们使用prevState,它始终使用状态的最新副本。

答案 1 :(得分:0)

您要在每个组件渲染调用上设置setTimeoutsetInterval

答案 2 :(得分:0)

您的超时/间隔将在每次重新渲染时触发。您必须将其放入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"

答案 3 :(得分:0)

这里有多个问题。

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

BrianThompson指出以下解决方案可以解决这些问题。我添加了一些评论来解释原因。

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(...)
},[])