【问题标题】:How does React.useState fire the containing function again?React.useState 如何再次触发包含函数?
【发布时间】:2020-03-08 03:15:27
【问题描述】:

假设我们有一个简单的功能组件:

export function MyComp(){

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

   setTimeout(() => setState({foo:2}, 45);

   return (
    <div> {state.foo} </div>
   )

}

如果setState 没有再次神奇地调用MyComp(),它将如何重新渲染?如果它再次神奇地调用MyComp(),它是如何工作的?

【问题讨论】:

    标签: reactjs react-hooks setstate


    【解决方案1】:

    在您的代码中,setState 不会神奇地调用您的组件,而是会触发重新渲染。因此,在您的情况下执行 setState(someValue) 是导致 MyComp 重新渲染的原因。你可以在 React 文档中阅读更多关于这个和钩子的信息:https://reactjs.org/docs/hooks-state.html#recap

    希望它能回答你的问题!

    【讨论】:

    • 抱歉,但它没有回答问题 - 必须调用 MyComp() 才能进行重新渲染,但鉴于 JS 的规则,目前尚不清楚这是如何发生的
    • 重新渲染由 React 内部处理。如果想详细了解流程,建议阅读这篇文章:medium.com/the-guild/…
    【解决方案2】:

    我不是 React 机制本身的专家,但使用一些 CS 概念的非常简化的版本背后的想法可能如下:

    首先,React 必须有一个应用程序中所有组件的列表,我们将用一个列表来简化它,但它实际上是一棵树:

    const nodes = <>
      <h1>My Counter</h1>
      <Counter />
      <div>
    </>
    

    所以此时,如果您的应用程序是扁平的,DummyReactDOM.render(nodes) 将创建这些节点的列表“:

    nodes = [ h1, Counter, div ]
    

    useState 被调用时会变得很棘手,React 会保存某种状态(记忆状态),例如 Counter 节点可能看起来像这样:

    function Counter() {
      [ counter, setCounter ] = useState(0);
    }
    

    这将为节点添加一个状态:

    node Counter = {
      state : {
        counter: 0
        next  : null
      }
    }
    

    现在,当我们单击组件并调用 setCounter(counter + 1) 时会发生什么? 下一个状态被添加到队列中:

    node Counter = {
      state : {
        counter : 0,
        next    : {
          state : {
            counter : 1, // <-- setCounter(counter + 1 happens)
            next    : null,
          }
        }
      }
    }
    
    // firstState.next -> secondState.next -> null
    

    现在,调度程序 是了解哪些组件需要重新渲染的主脑。这是一种非常不切实际但易于理解的方式:

    while (true) {
    
      // sleepForABit();
    
      if (currentNode.state.next !== null && currentNode.state !== currentNode.state.next) {
        currentNode.state = currentNode.state.next;
        currentNode.component.render(); // or functionalComponent() call
      }
    
      currentNode = currentNode.next
    }
    

    如果它是一个节点列表,这是一个简单的情况,但显然由于一个节点需要重新渲染,React 会检查它的子节点。重要的是要注意重新渲染并不是那么昂贵,昂贵的部分是重新创建 DOM 节点(并且在较小程度上更新它们),因此检查子节点的性能非常好。

    希望这会有所帮助!

    如果你想知道为什么我们可以在没有 counter = 0 的情况下执行 setCounter(counter + 1),React 保证在某些事件上重新渲染节点(onClick 是其中之一)。

    【讨论】:

    • 在基于类的组件中render() 会再次被调用,我假设如果状态发生变化。所以在功能性“组件”中,我会假设状态改变时再次调用该函数?这是我最初的假设,但我想这是错误的?
    • 对于类组件,render() 以及其他生命周期方法(例如 componentDidUpdate)确实会被调用。对于功能组件,整个函数都被调用,没有其他方法。这就是钩子存在的原因,例如useEffect的代码如果某些值没有改变,就不会再次运行。
    猜你喜欢
    • 1970-01-01
    • 2019-04-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2021-06-05
    • 2013-01-09
    • 1970-01-01
    • 2022-01-01
    相关资源
    最近更新 更多