我在看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 来深入研究
这是一个简短的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 中的调用堆栈。
我们如何在 DOMComponentWrapper 中结束
在 Component
的 mountComponent
方法中,我们有:
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/