【问题标题】:React hooks state in single useEffect [duplicate]在单个 useEffect 中反应钩子状态 [重复]
【发布时间】:2019-06-21 04:28:11
【问题描述】:

我正在尝试使用新的 React Hooks 功能,但遇到了一点问题。

Fiddle

我有一个useEffect,它调用setInterval,它会更新本地状态。像这样:

  const [counter, setCounter] = React.useState(0);

  React.useEffect(() => {
    const k = setInterval(() => {
        setCounter(counter + 1);
    }, 1000);
    return () => clearInterval(k);
  }, []);

  return (
    <div>Counter via state: {counter}<br/></div>
  );

它不能正常工作,因为计数器在第一次调用时被捕获,因此计数器停留在 1 值。

如果我使用 refs,则更新 ref,但不会调用重新渲染(在 UI 中只会看到 0 值):

  const counterRef = React.useRef(0);

  React.useEffect(() => {
    const k = setInterval(() => {
      counterRef.current += 1;
    }, 1000);
    return () => clearInterval(k);
  }, []);

  return (
    <div>Counter via ref: {counterRef.current}</div>
  );

我可以通过组合它们来制作我想要的东西,但它看起来确实不对:

  const [counter, setCounter] = React.useState(0);
  const counterRef = React.useRef(0);

  React.useEffect(() => {
    const k = setInterval(() => {
        setCounter(counterRef.current + 1);
      counterRef.current += 1;
    }, 1000);
    return () => clearInterval(k);
  }, []);

  return (
    <div>Counter via both: {counter}</div>
  );

你能告诉谁用钩子正确处理这种情况吗?

【问题讨论】:

  • @estus 是的,原来它是重复的。我最初的问题不包含 setInterval,在这里我添加它只是为了做一个简单的例子,但最终问题是一样的。

标签: reactjs react-hooks


【解决方案1】:

useRef 仅在不需要更新组件的情况下才有用。但是在异步useEffect 中访问当前状态的问题可以用相同的方法解决,即使用对象引用而不是不可变状态:

  const [state, setState] = React.useState({ counter: 0 });

  React.useEffect(() => {
    const k = setInterval(() => {
        state.counter++;
        setCounter(state);
    }, 1000);
    return () => clearInterval(k);
  }, []);

在 React 社区中不鼓励使用可变状态,因为它比不可变状态有更多的缺点。

与类组件中的setState 一样,状态更新函数可用于在状态更新期间获取当前状态:

  const [counter, setCounter] = React.useState(0);

  React.useEffect(() => {
    const k = setInterval(() => {
        setCounter(conter => counter + 1);
    }, 1000);
    return () => clearInterval(k);
  }, []);

或者,setTimeout 可用于每秒设置新的间隔:

  React.useEffect(() => {
    const k = setTimeout(() => {
        setCounter(counter + 1);
    }, 1000);
    return () => clearInterval(k);
  }, [counter]);

【讨论】:

  • 在文档中错过了这个,我的错。非常感谢!
【解决方案2】:

到目前为止,我已经多次遇到这种情况,我更喜欢的解决方案是使用useReducer 而不是useState,如下所示:

  const [counter, dispatch] = React.useReducer((state = 0, action) => {
    // better declared outside of the component
    if (action.type === 'add') return state + 1
    return state
  });

  React.useEffect(() => {
    const k = setInterval(() => {
        dispatch({ type: 'add' });
    }, 1000);
    return () => clearInterval(k);
  }, []);

  return (
    <div>Counter via state: {counter}<br/></div>
  );

虽然增加了一些样板,但它确实简化了“何时考虑我的变量?”。 更多这里https://reactjs.org/docs/hooks-reference.html#usereducer

【讨论】:

  • 解决方案非常简洁!老实说,很难决定哪个答案更准确。但是,如果我需要在那里更新多个状态,它可能会变得更加笨拙,但我想setState(function) 几乎是一样的。
  • @nidu IIRC,useState 内部使用useReducer,前者是更新状态的严肃方式,而后者类似于 Redux。选择一个适合你的风格。
猜你喜欢
  • 2020-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
  • 2020-12-20
相关资源
最近更新 更多