javascript - React 如何在状态更改后更新组件及其子组件?

标签 javascript reactjs dom virtual-dom

我在看Paul O Shannessy - Building React From Scratch

我非常了解安装过程,但我很难理解 React 如何更新组件及其子组件

协调器通过这种方式控制更新过程:

function receiveComponent(component, element) {
  let prevElement = component._currentElement;
  if (prevElement === element) {
    return;
  }

  component.receiveComponent(element);
}

Component.receiveComponent

 receiveComponent(nextElement) {
    this.updateComponent(this._currentElement, nextElement);
  }

这是 Component.updateComponent 方法:

  updateComponent(prevElement, nextElement) {
    if (prevElement !== nextElement) {
      // React would call componentWillReceiveProps here
    }

    // React would call componentWillUpdate here

    // Update instance data
    this._currentElement = nextElement;
    this.props = nextElement.props;
    this.state = this._pendingState;
    this._pendingState = null;

    let prevRenderedElement = this._renderedComponent._currentElement;
    let nextRenderedElement = this.render();
 
    if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
      Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
    } 
  }

这是在状态更改后更新组件的代码部分,我认为它也应该更新子组件,但我不明白这段代码是如何实现的,在安装过程中 React 实例化组件以进行潜水在树的更深处但这并没有发生在这里,我们需要找到第一个 HTML 元素然后我们可以改变我们的策略并在代码的另一个地方更新那个 HTML 元素,我找不到任何方法来找到任何 HTML这样的元素。

找到第一个 HTML 是停止这种无休止递归的方法,从逻辑上讲,这是我对代码的期望,在安装过程中以相同的方式停止递归,但在安装过程中,这需要组件实例化,因此我们可以委托(delegate)给协调器会发现我们处理的是 HTML 元素的包装器实例,而不是自定义组件的包装器实例,然后 React 可以将该 HTML 元素放入 DOM 中。

我不明白代码在更新过程中是如何工作的。我看到的这段代码不会深入到树中,我认为不会更新子元素,也不会让 React 找到第一个 HTML 元素,因此 React 可以更新 DOM 元素,不是吗?

这是 Github 上的代码库

最佳答案

我创建了一个 codesandbox 来深入研究

这里是 the codesandbox I created

这是一个简短的recording我打开调试器并看到调用堆栈。

工作原理

从您停止的地方开始,Component.updateComponent:

  updateComponent(prevElement, nextElement) {
  //...
    if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
      Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
  //...

Component.updateComponent 方法中调用 Reconciler.receiveComponent 调用 component.receiveComponent(element);

现在,此 component 引用 this._renderedComponent 并且不是 Component 的实例,而是 DOMComponentWrapper 的实例

下面是 DOMComponentWrapper 的 receiveComponent 方法:

  receiveComponent(nextElement) {
    this.updateComponent(this._currentElement, nextElement);
  }

  updateComponent(prevElement, nextElement) {
    // debugger;
    this._currentElement = nextElement;
    this._updateDOMProperties(prevElement.props, nextElement.props);
    this._updateDOMChildren(prevElement.props, nextElement.props);
  }

然后 _updateDOMChildren 结束调用子 render 方法。

这是我创建的用于挖掘的 codesandbox 中的调用堆栈。

call stack from setState until child render

我们如何在 DOMComponentWrapper 中结束

ComponentmountComponent 方法中,我们有:

let renderedComponent = instantiateComponent(renderedElement);
this._renderedComponent = renderedComponent;

instantiateComponent 中我们有:

  let type = element.type;

  let wrapperInstance;
  if (typeof type === 'string') {
    wrapperInstance = HostComponent.construct(element);
  } else if (typeof type === 'function') {
    wrapperInstance = new element.type(element.props);
    wrapperInstance._construct(element);
  } else if (typeof element === 'string' || typeof element === 'number') {
    wrapperInstance = HostComponent.constructTextComponent(element);
  }

  return wrapperInstance;

HostComponent 正在 dilithium.js 主文件中注入(inject) DOMComponentWrapper:

HostComponent.inject(DOMComponentWrapper);

HostComponent 只是一种代理,旨在反转控制并允许在 React 中使用不同的 Host。

这是注入(inject)方法:

function inject(impl) {
  implementation = impl;
}

construct 方法:

function construct(element) {
  assert(implementation);

  return new implementation(element);
}

当我们没有 DOMComponentWrapper 时

如果我们正在更新一系列非主机组件,例如:

const Child = <div>Hello</div>

const Parent = () => <Child />

Child 如何从 Parent 的更新中呈现?

Parent Component 具有以下内容:

  • _renderedComponent 是 Child 的一个实例(也是一个 Component)
    • renderedComponent 有一个 Child 实例,因为它获取“根”元素(由 render 方法返回的元素)的类型

所以 Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement) 将调用 Child 的 component.receiveComponent(element),后者又调用 this.updateComponent (this._currentElement, nextElement);(属于 Child)调用它的 render 方法(let nextRenderedElement = this.render();)

关于javascript - React 如何在状态更改后更新组件及其子组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70626534/

相关文章:

javascript - Node.js express : Passing data from URL and session into served JavaScript file

javascript - 如果没有输入焦点,则粘贴到默认文本区域

javascript - 无法在 anchor 内设置跨度的内容

javascript - 在 JavaScript 中确定鼠标指针位于哪个元素之上

php - JavaScript 返回 "is not a function"错误,但该方法在 Firebug 的响应中列出

javascript - 如何在 &lt;textarea&gt; 中按字母顺序排列? JavaScript 排序()?

javascript - jQuery从选择中删除选项

javascript - 即使 pure 总是更快,我什么时候应该使用 React 无状态组件?

reactjs - React : history. 听 - 如何不听?

javascript - 如何检测页面上是否未使用 CSS @font-face?