【问题标题】:What is the 'proper' way to update a react component after an interval with hooks?在使用钩子间隔后更新反应组件的“正确”方法是什么?
【发布时间】:2019-06-19 06:21:10
【问题描述】:

我正在使用 alpha 版本的 react 支持钩子,并且想验证我在间隔后更新组件中文本的方法而不在道具更改时渲染组件的次数超过所需次数.

编辑:为了清楚起见 - 这个组件在 formatTimeString 函数 (docs here) 中调用 moment(timepoint).fromNow(),所以更新并非完全没有必要,我保证!

我以前有:

const FromNowString = ({ timePoint, ...rest }) => {
  const [text, setText] = useState(formatTimeString(timePoint));

  useEffect(() => {
    setText(formatTimeString(timePoint));
    let updateInterval = setInterval(
      () => setText(formatTimeString(timePoint)),
      30000
    );
    return () => {
      clearInterval(updateInterval);
    };
  }, [timePoint]);

  // Note the console log here is so we can see when renders occur
  return (
    <StyledText tagName="span" {...rest}>
      {console.log('render') || text}
    </StyledText>
  );
};

这“有效” - 如果 props 更改,组件会正确更新,并且组件会在每个间隔更新,然而在安装时,并且当 props 更改时,组件将渲染两次。

这是因为useEffecttimePoint 的值发生变化时运行 渲染结果,并且在我的useEffect 回调中我立即调用setState 方法来触发额外的渲染。

显然,如果我删除对setText 的调用,组件似乎不会在道具更改时发生更改(直到间隔运行),因为text 仍然相同。

我终于意识到我可以通过设置一个我实际上并不需要的状态变量来触发渲染,如下所示:

const FromNowString = ({ timePoint, ...rest }) => {
  // We never actually use this state value
  const [, triggerRender] = useState(null);

  useEffect(() => {
    let updateInterval = setInterval(() => triggerRender(), 30000);
    return () => {
      clearInterval(updateInterval);
    };
  }, [timePoint]);

  return (
    <StyledText tagName="span" {...rest}>
      {console.log("render") || formatTimeString(timePoint)}
    </StyledText>
  );
};

这很完美,组件只在挂载时渲染一次,并且在 timePoint 属性更改时渲染一次,但感觉很hacky。这是正确的处理方式,还是我缺少什么?

【问题讨论】:

  • 不清楚这个间隔的目的是什么。由于 timePoint 不会随时间变化,因此它会使用相同的数据重新渲染组件。请详细说明组件的预期行为。首选方式取决于此。
  • @estus - 抱歉,我没有包括在内,因为它是并不真正需要的外围信息。我已经添加了为什么我需要在编辑中更新,见上文。

标签: reactjs react-hooks


【解决方案1】:

我认为这种方法看起来不错。我要做的主要更改是每次实际更改值,因此改为:

const FromNowString = ({ timePoint, ...rest }) => {
  const [, triggerRender] = useState(0);

  useEffect(() => {
    const updateInterval = setInterval(() => triggerRender(prevTriggerIndex => prevTriggerIndex + 1), 30000);
    return () => {
      clearInterval(updateInterval);
    };
  }, [timePoint]);

  return (
    <StyledText tagName="span" {...rest}>
      {console.log("render") || formatTimeString(timePoint)}
    </StyledText>
  );
};

我有两个理由建议进行此更改:

  • 我认为它在调试和/或验证正在发生的确切行为时会有所帮助。然后,您可以在开发工具中查看此状态,并准确了解您以这种方式触发了重新渲染的次数。
  • 另一个原因只是为了让查看此代码的人更有信心,相信它确实会按照预期执行。尽管setState 可靠地触发了重新渲染(并且 React 不太可能改变这一点,因为它会破坏太多),但查看此代码的人想知道“React 是否保证重新渲染如果 @ 987654323@ 调用不会导致状态发生任何变化?” setState 即使未更改也总是触发重新渲染的主要原因是因为可能在对现有状态进行更改后调用setState,但是如果现有状态为空并且没有任何内容传递给设置器,在这种情况下,React 可以知道自上次渲染以来状态没有改变并对其进行优化。我不会强迫某人深入研究 React 的确切行为或担心该行为是否会在未来发生变化,而是对状态进行实际更改。

【讨论】:

  • 感谢 Ryan,有趣的补充 - 如果我对这种方法没有遇到任何阻力,我会很快接受这个答案。
猜你喜欢
  • 2019-07-12
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 2018-08-08
  • 1970-01-01
  • 2021-09-12
  • 2019-10-19
  • 2020-07-12
相关资源
最近更新 更多