1. React 生命周期
1.1. 组件的生命周期
组件生命周期有三种阶段:初始化阶段(Mounting)、更新阶段(Updating)、析构阶段(Unmouting)。
初始化阶段:
constructor()
:初始化 state、绑定事件componentWillMount()
:在render()
之前执行,除了同构,跟 constructor 没啥差别render()
:用于渲染 DOM。如果有操作 DOM 或和浏览器打交道的操作,最好在下一个步骤执行。componentDidMount()
:在render()
之后立即执行,可以在这个函数中对 DOM 就进行操作,可以加载服务器数据,可以使用setState()
方法触发重新渲染
组件更新阶段:
componentWillReceiveProps(nextProps)
:在已挂载的组件接收到新 props 时触发,传进来的 props 没有变化也可能触发该函数,若需要实现 props 变化才执行操作的话需要自己手动判断componentShouldUpdate(nextProps,nextState)
:默认返回 true,我们可以手动判断需不需要触发 render,若返回 false,就不触发下一步骤componentWillUpdate()
:componentShouldUpdate
返回 true 时触发,在 render 之前,可以在里面进行操作 DOMrender()
:重渲染componentDidUpdate()
:render 之后立即触发
组件卸载阶段:
componentWillUnmount()
:在组件销毁之前触发,可以处理一些清理操作,如无效的 timers 等
1.2. 在哪些生命周期中可以修改组件的 state(setState)
componentDidMount
和componentDidUpdate
- constructor、componentWillMount 中 setState 会发生错误:setState 只能在 mounted 或 mounting 组件中执行
- componentWillUpdate 中 setState 会导致死循环
1.3. In which lifecycle event do you make AJAX requests and why
AJAX requests should go in the componentDidMount lifecycle event.
- Fiber, the next implementation of React’s reconciliation algorithm, will have the ability to start and stop rendering as needed for performance benefits. One of the trade-offs of this is that componentWillMount, the other lifecycle event where it might make sense to make an AJAX request, will be “non-deterministic”. What this means is that React may start calling componentWillMount at various times whenever it feels like it needs to. This would obviously be a bad formula for AJAX requests.
- You can’t guarantee the AJAX request won’t resolve before the component mounts. If it did, that would mean that you’d be trying to setState on an unmounted component, which not only won’t work, but React will yell at you for. Doing AJAX in componentDidMount will guarantee that there’s a component to update.
1.4. What does shouldComponentUpdate do and why is it important
Above we talked about reconciliation and what React does when setState is called. What shouldComponentUpdate does is it’s a lifecycle method that allows us to opt out of this reconciliation process for certain components (and their child components).
Why would we ever want to do this?
As mentioned above, “The end goal of reconciliation is to, in the most efficient way possible, update the UI based on new state.” If we know that a certain section of our UI isn’t going to change, there’s no reason to have React go through the trouble of trying to figure out if it should. By returning false from shouldComponentUpdate, React will assume that the current component, and all its child components, will stay the same as they currently are.
1.5. Mounting Lifecycle
When a component gets mounted to the DOM or unmounted from it.
只能在
componentDidMount
中 发送异步请求、setState
The mounting lifecycle consists of methods that are invoked when a component is mounted or unmounted. In other words, these methods allow you to initially set up state, make API calls, start and stop timers, manipulate the rendered DOM, initialize third-party libraries, and more.
The mounting lifecycle is slightly different depending upon whether you use ES6 class syntax or React.createClass
to create components. When you use createClass
, getDefaultProps
is invoked first to obtain the component's properties. Next, getInitialState
is invoked to initialize the state.
ES6 classes do not have these methods. Instead, default props are obtained and sent to the constructor as an argument. The constructor
is where the state is initialized. Both ES6 class constructors and getInitialState
have access to the properties and, if required, can use them to help define the initial state.
1.5.1. componentWillMount
I don't think it's good to setState in componentWillMount
I don't believe the 3rd judgement. If the api is too fast and returns the data even before component get mounted, setState won't work, although this is unlikely to happen.
- Calling
setState
before the component has rendered will not kick off the updating lifecycle. - Calling
setState
after the component has been rendered will kick off the updating lifecycle. - Note: with new React, you cannot make API calls in
componentWillMount
1.5.2. componentDidMount, componentWillUnmount
componentDidMount
is invoked just after the component has renderedcomponentWillUnmount
is invoked just before the component is unmounted.componentDidMount
is the only good place to make API requests. This method is invoked after the component has rendered, so anysetState
calls from this method will kick off the updating lifecycle and re-render the component.componentDidMount
is also a good place to initialize any third-party JavaScript that requires a DOM. For instance, you may want to incorporate a drag-and-drop library or a library that handles touch events. Typically, these libraries require a DOM before they can be initialized.To start background processes like intervals or timers. Any processes started in
componentDidMount
orcomponentWillMount
can be cleaned up incomponentWillUnmount
. You don't want to leave background processes running when they are not needed.
1.6. Updating Lifecycle
When a component receives new data and later.
The updating lifecycle is a series of methods that are invoked ==when a component's state changes or when new properties are received from the parent.== This lifecycle can be used to incorporate JavaScript before the component updates or to interact with the DOM after the update. Additionally, it can be used to improve the performance of an application because it gives you the ability to cancel unnecessary updates.
The updating lifecycle kicks off every time setState
is called. Calling setState
within the updating lifecycle other than componentWillReceiveProps
will cause an infinite recursive loop that results in a stack overflow error. Therefore, setState
can only be called in componentWillReceiveProps
, which allows the component to update state when its properties are updated.
1.6.1. componentWillReceiveProps(nextProps)
Triggered when the component receives new props from its parent component. This is the only method where setState
can be called.
当组件传入的 props 发生变化时调用.例如:父组件状态改变,给子组件传入了新的 prop 值。用于组件 props 变化后,更新 state。
1.6.2. shouldComponentUpdate(nextProps, nextState)
It's the update lifecycle's gatekeeper -- a predicate that can call off the update. This method can be used to ==improve performance== by only allowing necessary updates.
1.6.3. componentWillUpdate(nextProps, nextState)
Invoked just before the component updates. Similar to componentWillMount
, only it is invoked before each update occurs.
1.6.4. componentDidUpdate(prevProps, prevState)
Invoked just after the update takes place, after the call to render
. It is invoked after each update.
Parent.js
import React, { Component } from 'react';
import Child from './Child';
export default class App extends Component {
constructor(props) {
super(props);
console.log('Parent - constructor');
this.state = {
number: 100,
};
this.addHandler = this.addHandler.bind(this);
}
addHandler() {
this.setState((prevState) => ({
number: prevState.number + 1,
}));
}
componentWillMount() {
console.log('Parent - componentWillMount');
}
componentDidMount() {
console.log('Parent - componentDidMount');
}
render() {
console.log('Parent - render');
return (
<div>
<button onClick={this.addHandler}>{this.state.number}</button>
<Child number={this.state.number} />
</div>
);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Parent - shouldComponentUpdate');
return true;
}
componentWillReceiveProps(nextProps) {
console.log('Parent - componentWillReceiveProps');
}
componentWillUpdate(nextProps, nextState) {
console.log('Parent - componentWillUpdate');
}
componentDidUpdate(prevProps, prevState) {
console.log('Parent - componentDidUpdate');
}
componentWillUnmount() {
console.log('Parent - componentWillUnmount');
}
}
Child.js
import React, { Component } from 'react';
export default class Child extends Component {
constructor(props) {
super(props);
console.warn('Child - constructor');
}
componentWillMount() {
console.warn('Child - componentWillMount');
}
componentDidMount() {
console.warn('Child - componentDidMount');
}
render() {
console.warn('Child - render');
return <div>Props from Parent: {this.props.number}</div>;
}
shouldComponentUpdate(nextProps, nextState) {
console.warn('Child - shouldComponentUpdate');
return true;
}
componentWillReceiveProps(nextProps) {
console.warn('Child - componentWillReceiveProps');
}
componentWillUpdate(nextProps, nextState) {
console.warn('Child - componentWillUpdate');
}
componentDidUpdate(prevProps, prevState) {
console.warn('Child - componentDidUpdate');
}
componentWillUnmount() {
console.warn('Child - componentWillUnmount');
}
}
Initial load:
点击 parent button: