【问题标题】:Adding seconds to Intl.dateTimeFormat with React useEffect, useState, and setInterval使用 React useEffect、useState 和 setInterval 将秒数添加到 Intl.dateTimeFormat
【发布时间】:2021-07-01 02:42:11
【问题描述】:

我遇到了一个奇怪的问题,在 setState() 钩子函数中更改克隆的顺序会改变预期的行为。

我试图每秒增加一秒的值。但是这样做会直接导致秒数增加 2 而不是 1。

这行得通

const [value, setValue] = useState(new Date());

useEffect(() => {
  const interval = setInterval(
  () =>
    setValue((value) => {
      const clonedDate = new Date(value.getTime());
      clonedDate.setSeconds(clonedDate.getSeconds() + 1); // Add one second to the time
      return clonedDate;
    }),
  1000
  );
  return () => {
    clearInterval(interval);
  };
}, []);

这增加了两秒而不是一秒

const [value, setValue] = useState(new Date());

useEffect(() => {
  const interval = setInterval(
  () =>
    setValue((value) => {
      value.setSeconds(value.getSeconds() + 1);
      const clonedDate = new Date(value.getTime());
      return clonedDate;
    }),
  1000
  );
  return () => {
    clearInterval(interval);
  };
}, []);

【问题讨论】:

  • 不确定,但在第一个中,您每次都在创建新对象,然后增加秒数,但在第二个中,您在现有对象中增加,然后创建新对象..可能就像它持有引用第二个中的前一个值对象

标签: reactjs date settimeout


【解决方案1】:

我唯一清楚的是,在第二个版本中,状态突变显然正在发生,但老实说,我并不清楚确切的位置。似乎即使您正在创建一个 new javascript Date 对象,它仍在引用前一个数据对象的属性。

考虑以下表现出相同行为的示例:

function App() {
  const [value1, setValue1] = useState({ c: 0 });
  const [value2, setValue2] = useState({ c: 0 });

  useEffect(() => {
    const interval = setInterval(
      () =>
        setValue1((value) => {
          const clonedValue = { ...value };  // shallow copy first
          clonedValue.c = clonedValue.c + 1; // update copy
          return clonedValue;                // return copy
        }),
      1000
    );
    return () => {
      clearInterval(interval);
    };
  }, []);
  
  useEffect(() => {
    const interval = setInterval(
      () =>
        setValue2((value) => {
          value.c = value.c + 1;            // mutate current
          const clonedValue = { ...value }; // shallow copy
          return clonedValue;               // return copy
        }),
      1000
    );
    return () => {
      clearInterval(interval);
    };
  }, []);

  return (
    <div className="App">
      <h1>Non-mutaiton Version</h1>
      {value1.c}

      <h1>Mutation Version</h1>
      {value2.c}
    </div>
  );
}

不过,有趣的是,如果您从 App 附近删除 React.StrictMode,两者的性能相同。

StrictMode 目前有助于:

  • 识别生命周期不安全的组件
  • 关于旧字符串引用 API 使用的警告
  • 关于不推荐使用 findDOMNode 的警告
  • 检测意外副作用
  • 检测旧版上下文 API

Detecting unexpected side effects

严格模式无法自动为您检测副作用,但可以通过使它们更具确定性来帮助您发现它们。这是通过有意双重调用以下函数来完成的:

  • 类组件constructorrendershouldComponentUpdate方法
  • 类组件静态getDerivedStateFromProps方法
  • 功能组件体
  • 状态更新函数(setState 的第一个参数)
  • 函数传递给 useStateuseMemouseReducer

在 StrictMode 和非 StrictMode 下运行原始日期对象的演示:

【讨论】:

    【解决方案2】:

    问题在于下面的行它正在改变值对象。不需要调用 value.setSeconds,value.getSeconds()+1 itSelf 就足以让秒数加 1。

    Replace below line of code  -
    
    
    
       const interval = setInterval(
          () =>
            setValue2((value2) => {
              let second = value2.getSeconds() +1;
             
              //value2.setSeconds(second);
               let newTime = new Date();
              newTime.setSeconds(second);
              const clonedDate = new Date(newTime.getTime());
              return clonedDate;
            }),
          1000
        );
    

    【讨论】:

    • @Drew Rees ...如果您取消注释已注释的代码 (//value2.setSeconds(second);),它将再次开始增加 2 秒。
    【解决方案3】:

    再试一次,似乎这段代码现在神奇地工作了,我只能假设有问题的组件在没有正确键的情况下被映射,或者以某种方式被调用了两次。为大家投入的时间道歉。

    【讨论】:

      猜你喜欢
      • 2021-02-17
      • 2021-11-17
      • 2022-01-12
      • 2020-03-23
      • 2022-07-19
      • 2022-12-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多