React 生命周期方法 - 嵌套组件
这篇笔记讨论了 React 生命周期方法与嵌套组件,这些是我觉得值得记录的内容。
引言
这篇笔记讨论了 React 生命周期方法 与嵌套组件,这些是我觉得值得记录的内容。
背景
在之前的笔记中,我讨论了在非嵌套的单个 React 组件中生命周期方法的执行顺序。在这篇笔记中,我将通过一个示例来展示带有嵌套 React 组件的生命周期方法的执行顺序。
附带的是一个 Java Maven 项目。示例实现在“nested-component-life-cycle.html”文件中。如果您有兴趣探索 Maven 项目,可以查看这个链接和这个链接。如果您不使用 Java,也没关系,因为附件是一个静态的“html”文件,您可以从任何 Web 服务器加载它。当“nested-component-life-cycle.html”加载时,它将在浏览器中显示为以下内容:
- React 组件嵌套了 3 层;
- 每个复选框由相应组件的“shouldComponentUpdate()”方法使用。如果选中,该方法将返回 true,否则返回 false。
尽管这篇笔记的主题很简单,但我还是建议您先查看更简单的非嵌套情况,这将使您更容易阅读这篇笔记。如果您是 React 新手,我强烈建议您查看它,以便熟悉 React 语法和这篇笔记的上下文。我已经尽力使这篇笔记简单化,但我必须承认这是一个相对高级的主题。
“nested-component-life-cycle.html”文件
嵌套结构
为了更轻松地实现嵌套组件,我将嵌套结构保存在一个 Json 对象中。
var tree = {
label: 'Root',
bgColor: 'lightpink',
level: 0,
children: [
{
label: 'A',
bgColor: 'lightblue',
level: 1,
children: [
{
label: 'A-1',
bgColor: 'lightyellow',
level: 2
},
{
label: 'A-2',
bgColor: 'lightyellow',
level: 2
}
]
},
{
label: 'B',
bgColor: 'lightblue',
level: 1,
children: [
{
label: 'B-1',
bgColor: 'lightyellow',
level: 2
},
{
label: 'B-2',
bgColor: 'lightyellow',
level: 2
}
]
}
]
};
- 这个 Json 对象表示一个从“Root”到叶节点“A-1”、“A-2”、“B-1”和“B-2”的树形结构;
- 我将 遍历 Json 树来生成嵌套的 React 组件。
复选框
通过遍历 Json 树,以下代码创建了一个 React 类,该类可用于将所有复选框渲染到浏览器。每个复选框的 HTML 属性“id”将是“ck”+ Json 节点的标签。
var chkClass = function(node) {
return React.createClass({
render: function() {
var renderNestedNode = function(node) {
var label = node.label;
var children = node.children;
var o = {type: 'checkbox',
id: 'ck' + label,
defaultChecked: true
};
var current = React.createElement('label', null,
React.createElement('input', o), label);
if (!children || children.length == 0) {
return React.createElement('ul', null,
React.createElement('li', null, current));
}
var lis = [];
lis.push(React.createElement('li', {key: 0}, current));
for (var i = 0; i < children.length; i++) {
lis.push(React.createElement('li', {key: i + 1},
renderNestedNode(children[i])));
}
return React.createElement('ul', null, lis);
};
return renderNestedNode(node);
}
});
}(tree);
用于测试的嵌套 React 类
通过遍历 Json 树,以下代码创建了一个嵌套的 React 类。嵌套类中的每个组件都有自己的生命周期方法实现。我们将使用这个嵌套类来测试生命周期方法的执行顺序。
var nestedClass = function(node) {
var getReactClass = function(node, children) {
var label = node.label;
var bgColor = node.bgColor;
var level = node.level;
var indent = '';
for (var i = 0; i < level; i++) {
indent = indent + ' ';
}
var log = function(method) {
console.log(indent + label + ': ' + method);
};
return React.createClass({
getInitialState: function() {
log('getInitialState');
return {updateid: 0};
},
componentWillMount: function() {
log('componentWillMount');
},
componentDidMount: function() {
log('componentDidMount');
},
shouldComponentUpdate: function(nextProp, nextState) {
var id = 'ck' + label;
var ckd = document.getElementById(id).checked;
if (ckd) {
log('shouldComponentUpdate - true');
return true;
}
log('shouldComponentUpdate - false');
return false;
},
componentWillUpdate: function() {
log('componentWillUpdate');
},
componentDidUpdate: function() {
log('componentDidUpdate');
},
componentWillUnmount: function() {
log('componentWillUnmount');
},
update: function() {
this.setState({updateid: this.state.updateid + 1});
},
unmount: function() {
var node = ReactDOM.findDOMNode(this);
ReactDOM.unmountComponentAtNode(node.parentNode);
},
render: function() {
var updateBtn = React.createElement('button',
{onClick: this.update}, 'Update');
var unmountBtn = React.createElement('button',
{onClick: this.unmount}, 'Unmount');
var buttons = React.createElement('div',
null, updateBtn, unmountBtn);
var updateLbl = label
+ ' update - ' + this.state.updateid;
var cElements = [];
if (children) {
for (var i = 0; i < children.length; i++) {
var c = React.createElement(children[i]);
cElements.push(React.createElement('div', {key: i}, c));
}
}
return React.createElement('div',
{className: 'border', style: {backgroundColor: bgColor}},
updateLbl, buttons, cElements);
}
});
};
var getNestedClass = function(node) {
var children = node.children;
if (!children || children.length == 0) {
return getReactClass(node);
}
var classes = [];
for (var i = 0; i < children.length; i++) {
classes.push(getNestedClass(children[i]));
}
return getReactClass(node, classes);
};
return getNestedClass(node);
}(tree);
挂载到 DOM
以下代码在页面在浏览器中加载时,“window.onload”事件中将 React 类挂载到 DOM。
<div id="checks" class="border"></div>
<div id="content"></div>
window.onload = function() {
ReactDOM.render(React.createElement(chkClass),
document.getElementById('checks'));
ReactDOM.render(React.createElement(nestedClass),
document.getElementById('content'));
};
React 生命周期方法的执行顺序
挂载过程
在 Chrome 浏览器中,您可以通过“CTL-SHIT-J”启动开发者工具。如果您将“nested-component-life-cycle.html”加载到浏览器中,并在开发者工具的“console”选项卡中查看,您可以看到以下日志:
- 在挂载过程中,每个组件的“getInitialState()”和“componentWillMount()”方法都会被调用。这些方法的调用顺序是按照深度优先方式从“Root”到叶节点;
- 在调用完所有组件的“getInitialState()”和“componentWillMount()”方法之后,会调用每个组件的“componentDidMount()”方法。这些方法的调用顺序是从叶节点到“Root”;
- “Root”组件的“componentDidMount()”方法最后被调用。此时,HTML 内容已完全渲染到浏览器中。
更新过程
如果我们点击任何一个“Update”按钮,我们就可以查看与更新过程相关的生命周期方法。以下是我们点击“A-2”组件中的“Update”按钮时产生的日志:
日志显示只有“A-2”组件被更新了。更新子组件不会触发父组件的更新。如果我们现在点击“Root”组件中的“Update”按钮,我们可以看到以下日志:
- 如果我们触发父组件的更新,所有子组件都将被更新;
- 每个组件的“shouldComponentUpdate()”和“componentWillMount()”方法将按照深度优先方式调用;
- 在所有组件的“shouldComponentUpdate()”和“componentWillMount()”方法被调用之后,“componentDidUpdate()”方法从叶节点到“Root”顺序被调用;
- “Root”组件的“componentDidUpdate()”最后被调用。此时,HTML 内容已完全更新到浏览器中。
如果我们取消选中与组件“A”相关的复选框,然后点击“Root”组件中的“Update”按钮,我们可以看到以下日志:
组件“A”中的“shouldComponentUpdate()”方法返回 false,这阻止了组件“A”的更新。它也阻止了“A”中任何子组件的更新。
卸载过程
如果我们点击任何一个“Unmount”按钮,我们就开始了一个元素的卸载过程。但是,如果我们点击一个不是“Root”元素的“Unmount”按钮,我们会收到一个错误消息,告诉我们不能卸载子元素。
如果我们点击“Root”元素中的“Unmount”按钮,我们可以看到以下日志:
- 每个元素的“componentWillUnmount()”方法按照深度优先方式调用;
- 当卸载过程完成时,React 渲染的所有 HTML 内容都已从 DOM 中清除。
关注点
- 这篇笔记讨论了 React 生命周期方法与嵌套组件;
- 如果您仅使用 React 创建网页,这个主题可能对您不重要。但如果您想使用第三方或自己的 Javascript 库来更新 DOM,生命周期方法及其执行顺序将发挥重要作用;
- 希望您喜欢我的博文,并希望这篇笔记能以某种方式帮助您。
历史
首次修订 - 2016 年 6 月 9 日。