React Router被拆分成三个包:react-router, react-router-dom, react-router-native
先了解一个简单的东西,组件在ES6和ES5的不同写法:
//ES6
class Greeting extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
ES5:
React.createClass({
render() {
return Hello, {this.props.name}
;
}
})
下面的例子中会使用ES6的写法去学习组件生命周期。下面例子的源码。
给大家看一下整个用于验证的界面。
//index.js
import React from 'react';
import ReactDOM from 'react-dom'
import './index.css';
import App from "./App";
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render((
), document.getElementById('root'))
//App.js
import React, {Component} from 'react';
import './App.css';
import Link from "react-router-dom/es/Link";
import About from "./containers/about";
import Inbox from "./containers/inbox";
import Switch from "react-router-dom/es/Switch";
import Route from "react-router-dom/es/Route";
class App extends Component {
render() {
return (
App
- About
- Inbox
- Message
)
}
}
export default App;
//inbox.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import Message from "./message";
class Inbox extends Component {
constructor(props) {
super(props);
this.state = {
num: Math.random() * 100
};
}
shouldComponentUpdate() {
console.log("parent shouldComponentUpdate");
return true; // 记得要返回true
}
propsChange() {
console.info("更新父组件state");
this.setState({
num: Math.random() * 100
});
}
setLifeCycleState() {
console.info("更新子组件state");
this.refs.rLifeCycle.setTheState();
}
forceLifeCycleUpdate() {
console.info("强制更新子组件");
this.refs.rLifeCycle.forceItUpdate();
}
parentForceUpdate() {
console.info("强制更新父组件");
this.forceUpdate();
}
render() {
console.log("parent render")
return (
);
}
}
export default Inbox;
//message.js
import React, {Component} from 'react';
class Message extends Component {
constructor(props) {
super(props);
console.log("constructor");
this.state = {str: "hello"};
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps");
return {str: "getDerivedStateFromProps update state"};
}
// UNSAFE_componentWillMount() {
// console.log("UNSAFE_componentWillMount()");
// }
componentDidMount() {
console.log("componentDidMount");
}
shouldComponentUpdate() {
console.log("shouldComponentUpdate");
return true; // 记得要返回true
}
getSnapshotBeforeUpdate() {
console.log("getSnapshotBeforeUpdate");
return true;
}
// componentWillUpdate() {
// console.log("componentWillUpdate");
// }
componentDidUpdate() {
console.log("componentDidUpdate");
}
componentWillUnmount() {
console.log("componentWillUnmount");
}
setTheState() {
let s = "hello";
if (this.state.str === s) {
s = "HELLO";
}
this.setState({
str: s
});
}
forceItUpdate() {
this.forceUpdate();
}
render() {
console.log("render");
return (
Props:{this.props.num}
State:{this.state.str}
);
}
}
export default Message;
//about.js
import React, { Component } from 'react';
class About extends Component {
render() {
return (
About
);
}
}
export default About;
constructor -> getDerivedStateFromProps -> UNSAFE_componentWillMount(不能与getDerivedStateFromProps共存) -> render -> componentDidMount
class Message extends Component {
constructor(props) {
super(props);
console.log("Message constructor");
}
....
}
装换成ES5的代码为
var Message = function(_Component) {
_inherits(Message, _Component); //将Message的prototype 指向给ReactComponent.prototype。但是构造函数会重写为下面的Message方法。
function Message(props) {
_classCallCheck(this, Message); //判断当前的对象是不是使用Message方法new 出来的实例。
var _this = _possibleConstructorReturn(this, (Message.__proto__ || Object.getPrototypeOf(Message)).call(this, props)); //对应super(props)
console.log("Message constructor");
return _this;
}
}
执行下面这行代码之前我们看一下,当前this,如下图:
var _this = _possibleConstructorReturn(this, (Message.__proto__ || Object.getPrototypeOf(Message)).call(this, props))
执行完上面这行代码之后是为了形成this对象,如下图,这也是为什么要执行super(props)的目的。
constructor(props) {
super(props);
this.state = {
color: props.initialColor //提示: props不会被在当前组件被修改,除非是父组件重新渲染传入新的props。
};
}
组件装配之前,我的理解就是初始化这个组件,但是在组件的生命周期,当父组件state变化导致render的前提下,一般情况下构造函数不会执行,除非当前组件的key发生了变化。如下:
基本上每次父组件传入进来的key都不同,导致会创建新的组件。
如果定义了getDerivedStateFromProps后,又定义了componentWillReceiveProps。那么,只有前者会被调用,并且你会收到一个警告。
static getDerivedStateFromProps(nextProps, prevState)
在装配了的组件接收到新属性前调用。若你需要更新状态响应属性改变(例如,重置它),你可能需对比this.props和prevState并在该方法中返回一个对象来更新状态,或者返回null来表明新属性不需要更新任何状态。这里返回的更细状态就是以前this.setState(object)中的object。
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.currentRow === prevState.lastRow) {
return null;
}
return {
lastRow: nextProps.currentRow,
isCrollingDown: nextProps.curentRow > prevState.lastRow
}
}
* componentWillMount / UNSAFE_componentWillMount 可以用到React 16.4。在React 17里将被彻底移除。
UNSAFE_componentWillMount()在装配发生前被立刻调用。其在render()之前被调用,因此在这方法里同步地设置状态将不会触发重渲。
避免在该方法中引入任何的副作用或订阅。对于这些使用场景,我们推荐使用constructor()来替代。
这是唯一的会在服务端渲染调起的生命周期钩子函数。
* 如果和上面的getDerivedStateFromProps同时存在会报错:
index.js:2178 Warning: Unsafe legacy lifecycles will not be called for components using new component APIs.
Message uses getDerivedStateFromProps() but also contains the following legacy lifecycles:
UNSAFE_componentWillMount
The above lifecycles should be removed. Learn more about this warning here:
我在网上看到: componentWillMount--使用componentDidMount代替,其实看到这里真的有疑问说为啥能代替?一个发生在render前,一个发生在render后。如果存在设置state,会有两次渲染和一次渲染的区别。真的不懂这句话的含义,望有知道的告知。
当被调用时,其应该检查this.props 和 this.state并返回以下类型中的一个:
React元素。 通常是由 JSX 创建。该元素可能是一个原生DOM组件的表示,如,或者是一个你定义的合成组件。
字符串和数字。 这些将被渲染为 DOM 中的 text node。
Portals。 由 ReactDOM.createPortal 创建。
null。 什么都不渲染。
布尔值。 什么都不渲染。(通常存在于 return test &&
当返回null 或 false时,ReactDOM.findDOMNode(this) 将返回 null。
render()函数应该纯净,意味着其不应该改变组件的状态,其每次调用都应返回相同的结果,同时不直接和浏览器交互。若需要和浏览器交互,将任务放在componentDidMount()阶段或其他的生命周期方法。保持render() 方法纯净使得组件更容易思考。
* 若 shouldComponentUpdate()返回false,render()函数将不会被调用。在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。若你需要从远端加载数据,这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重渲。
这一方法是一个发起任何订阅的好地方。如果你这么做了,别忘了在componentWillUnmount()退订。
在这个方法中调用setState()将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使render()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。然而,它对于像模态框和工具提示框这样的例子是必须的。这时,在渲染依赖DOM节点的尺寸或者位置的视图前,你需要先测量这些节点。
getDerivedStateFromProps -> shouldComponentUpdate -> componentWillUpdate(与getDerivedStateFromProps & getSnapshotBeforeUpdate 不能共存) -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
当他们状态改变时,返回false 并不能阻止子组件重渲。
当前,若shouldComponentUpdate()返回false,而后UNSAFE_componentWillUpdate(),render(), 和 componentDidUpdate()将不会被调用。注意,在未来React可能会将shouldComponentUpdate()作为一个线索而不是一个严格指令,返回false可能仍然使得组件重渲。
意思为当父子组件存在,父组件当前函数返回值为false的时候,当前组件的state变化也不会引起重新渲染。但是不会影响子组件的state变化导致的子组件的重新渲染。
当组件被更新时,使用该方法是操作DOM的一次机会。这也是一个适合发送请求的地方,要是你对比了当前属性和之前属性(例如,如果属性没有改变那么请求也就没必要了)。
*若shouldComponentUpdate()返回false,componentDidUpdate()将不会被调用。
componentWillUpdate(与后面getSnapshotBeforeUpdate不能共存) -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
默认情况,当你的组件或状态发生改变,你的组件将会重渲。若你的render()方法依赖其他数据,你可以通过调用forceUpdate()来告诉React组件需要重渲。
调用forceUpdate()将会导致组件的 render()方法被调用,并忽略shouldComponentUpdate()。这将会触发每一个子组件的生命周期方法,涵盖,每个子组件的shouldComponentUpdate() 方法。若当标签改变,React仅会更新DOM。
通常你应该尝试避免所有forceUpdate() 的用法并仅在render()函数里从this.props和this.state读取数据。
错误边界是React组件,并不是损坏的组件树。错误边界捕捉发生在子组件树中任意地方的JavaScript错误,打印错误日志,并且显示回退的用户界面。错误边界捕捉渲染期间、在生命周期方法中和在它们之下整棵树的构造函数中的错误。
如果定义了这一生命周期方法,一个类组件将成为一个错误边界。在错误边界中调用setState()让你捕捉当前树之下未处理的JavaScript错误,并显示回退的用户界面。只使用错误边界来恢复异常,而不要尝试将它们用于控制流。