import React, { Component } from 'react' export default class LifeCycle extends Component { //// props = {age:10,name:'计数器'} static defaultProps = { name:'计数器' } constructor(props){ //Must call super constructor in derived class before accessing 'this' or returning from derived constructor super();//this.props = props; this.state = {number:0,users:[]};//初始化默认的状态对象 console.log('1. constructor 初始化 props and state'); } //componentWillMount在渲染过程当中可能会执行屡次 componentWillMount(){ console.log('2. componentWillMount 组件将要挂载'); //localStorage.get('userss'); } //componentDidMount在渲染过程当中永远只有执行一次 //通常是在componentDidMount执行反作用,进行异步操做 componentDidMount(){ console.log('4. componentDidMount 组件挂载完成'); fetch('https://api.github.com/users').then(res=>res.json()).then(users=>{ console.log(users); this.setState({users}); }); } shouldComponentUpdate(nextProps,nextState){ console.log('Counter',nextProps,nextState); console.log('5. shouldComponentUpdate 询问组件是否须要更新'); return true; } componentWillUpdate(nextProps, nextState){ console.log('6. componentWillUpdate 组件将要更新'); } componentDidUpdate(prevProps, prevState)){ console.log('7. componentDidUpdate 组件更新完毕'); } add = ()=>{ this.setState({number:this.state.number}); }; render() { console.log('3.render渲染,也就是挂载') return ( <div style={{border:'5px solid red',padding:'5px'}}> <p>{this.props.name}:{this.state.number}</p> <button onClick={this.add}>+</button> <ul> { this.state.users.map(user=>(<li>{user.login}</li>)) } </ul> {this.state.number%2==0&&<SubCounter number={this.state.number}/>} </div> ) } } class SubCounter extends Component{ constructor(props){ super(props); this.state = {number:0}; } componentWillUnmount(){ console.log('SubCounter componentWillUnmount'); } //调用此方法的时候会把新的属性对象和新的状态对象传过来 shouldComponentUpdate(nextProps,nextState){ console.log('SubCounter',nextProps,nextState); if(nextProps.number%3==0){ return true; }else{ return false; } } //componentWillReceiveProp 组件收到新的属性对象 componentWillReceiveProps(){ console.log('SubCounter 1.componentWillReceiveProps') } render(){ console.log('SubCounter 2.render') return( <div style={{border:'5px solid green'}}> <p>{this.props.number}</p> </div> ) } } 复制代码
static getDerivedStateFromProps
static getDerivedStateFromProps(nextProps,prevState)
:接收父组件传递过来的 props
和组件以前的状态,返回一个对象来更新 state
或者返回 null
来表示接收到的 props
没有变化,不须要更新 state
props
映射 到子组件的 state
上面,这样组件内部就不用再经过 this.props.xxx
获取属性值了,统一经过 this.state.xxx
获取。映射就至关于拷贝了一份父组件传过来的 props
,做为子组件本身的状态。注意:子组件经过 setState
更新自身状态时,不会改变父组件的 props
componentDidUpdate
,能够覆盖 componentWillReceiveProps
的全部用法props
时会被调用props
、组件状态更新时会被调用import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> <AAA /> </div> ); } class AAA extends React.Component { state = { age: 666 }; add = () => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <div> <ChildA onChangeParent={this.add} age={this.state.age} /> </div> ); } } class ChildA extends React.Component { state = { num: 888 }; // 根据新的属性对象派生状态对象 // nextProps——新的属性对象 prevState——旧的状态对象 static getDerivedStateFromProps(nextprops, state) { console.log('props',nextprops); // 返回一个对象来更新 state 或者返回 null 来表示接收到的 props 不须要更新 state if (nextprops.age !== state.age) { console.log("更新吧"); return { onChangeParent:nextprops.onChangeParent, age: nextprops.age, // 注意:这里不须要把组件自身的状态也放进来 // num:state.num }; } return null; } add = () => { this.setState({ num: this.state.num + 1 }); }; render() { const { onChangeParent } = this.state; console.log('state',this.state); return ( <> <div onClick={onChangeParent}>change</div> <div onClick={this.add}>add</div> </> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)
:接收父组件传递过来的 props
和组件以前的状态,今生命周期钩子必须有返回值,返回值将做为第三个参数传递给 componentDidUpdate
。必须和 componentDidUpdate
一块儿使用,不然会报错render
以后、更新 DOM
和 refs
以前DOM
和 refs
以前,从 DOM
中捕获一些信息(例如滚动位置)componentDidUpdate
, 能够覆盖 componentWillUpdate
的全部用法import React, { Component } from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> <GetSnapshotBeforeUpdate /> </div> ); } class GetSnapshotBeforeUpdate extends Component { constructor(props) { super(props); this.wrapper = React.createRef(); this.state = { messages: [] }; } componentDidMount() { setInterval(() => { this.setState({ messages: ["msg:" + this.state.messages.length, ...this.state.messages] }); //this.setState({messages:[...this.state.messages,this.state.messages.length]}); }, 1000); } getSnapshotBeforeUpdate() { // 返回更新内容的高度 300px return this.wrapper.current.scrollHeight; } componentDidUpdate(prevProps, prevState, prevScrollHeight) { this.wrapper.current.scrollTop = this.wrapper.current.scrollTop + (this.wrapper.current.scrollHeight - prevScrollHeight); } render() { let style = { height: "100px", width: "200px", border: "1px solid red", overflow: "auto" }; return ( <ul style={style} ref={this.wrapper}> {this.state.messages.map((message, index) => ( <li key={index}>{message}</li> ))} </ul> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
componentWillMount
,componentWillReceiveProps
,componentWillUpdate
这三个生命周期由于常常会被误解和滥用,因此被称为 不安全(不是指安全性,而是表示使用这些生命周期的代码,有可能在将来的 React 版本中存在缺陷,可能会影响将来的异步渲染) 的生命周期。UNSAFE_componentWillMount
,UNSAFE_componentWillReceiveProps
和 UNSAFE_componentWillUpdate
。(旧的生命周期名称和新的别名均可以在此版本中使用)componentWillMount
,componentWillReceiveProps
和 componentWillUpdate
启用弃用警告。(旧的生命周期名称和新的别名均可以在此版本中使用,但旧名称会记录DEV模式警告)dom
挂载以前的虚拟 dom
构建阶段,也就是要被去掉的三个生命周期 componentWillMount
,componentWillReceiveProps
和 componentWillUpdate
。(从这个版本开始,只有新的“UNSAFE_”生命周期名称将起做用)props
改变时,如何再次执行请求数据、更改状态等操做componentWillReceiveProps
class ExampleComponent extends React.Component { state = { externalData: null, }; componentDidMount() { this._loadAsyncData(this.props.id); } componentWillReceiveProps(nextProps) { // 当父组件的 props 改变时,从新请求数据 if (nextProps.id !== this.props.id) { this.setState({externalData: null}); this._loadAsyncData(nextProps.id); } } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } _loadAsyncData(id) { this._asyncRequest = asyncLoadData(id).then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); } } 复制代码
getDerivedStateFromProps
+ componentDidUpdate
加载数据class ExampleComponent extends React.Component { state = { externalData: null, }; static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { return { externalData: null, prevId: nextProps.id, }; } return null; } componentDidMount() { this._loadAsyncData(this.props.id); } // 借助 componentDidUpdate componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } _loadAsyncData(id) { this._asyncRequest = asyncLoadData(id).then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); } } 复制代码
getDerivedStateFromProps
更改状态import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> <AAA /> </div> ); } class AAA extends React.Component { state = { age: 66 }; add = () => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <div> <ChildA onChangeParent={this.add} age={this.state.age} /> </div> ); } } class ChildA extends React.Component { state = { num: 88 }; static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.age !== prevState.age) { return { age: nextProps.age }; } return null; } add = () => { this.setState({ num: this.state.num + 1 }); }; render() { const { onChangeParent } = this.props; console.log("render", this.state); return ( <> <div onClick={onChangeParent}>change</div> <div onClick={this.add}>add</div> </> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
componentDidUpdate
的写法getDerivedStateFromProps
或者 componentWillReceiveProps
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> <AAA /> </div> ); } class AAA extends React.Component { state = { age: 66 }; add = () => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <div> <ChildA onChangeParent={this.add} age={this.state.age} /> </div> ); } } class ChildA extends React.Component { state = { num: 88, age: this.props.age }; add = () => { this.setState({ num: this.state.num + 1 }); }; componentDidUpdate() { if (this.props.age !== this.state.age) { console.log("componentDidUpdate", this.props.age); this.setState({ age: this.props.age }); } } render() { const { onChangeParent } = this.props; console.log("render", this.state); return ( <> <div onClick={onChangeParent}>change</div> <div onClick={this.add}>add</div> </> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
key
,来从新初始化组件 在线 demodiff
props
的更改的状况(巴不得上去就给他个膝盖重锤 😂😂😂),可使用 key
来快速实现class ExampleComponent extends React.Component { state = { id: '123456', }; render(){ const {id} = this.state; // 当 id 变化时,key 也随之改变,那么组件就会从新初始化 return <ExampleComponent key={id} id={id}/>; } } class ExampleComponent extends React.Component { state = { externalData: null, }; // 不须要使用 getDerivedStateFromProps 或者 componentWillReceiveProps // static getDerivedStateFromProps(nextProps, prevState) { // if (nextProps.id !== prevState.prevId) { // return { // externalData: null, // prevId: nextProps.id, // }; // } // return null; // } componentDidMount() { this._loadAsyncData(this.props.id); } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } _loadAsyncData(id) { this._asyncRequest = asyncLoadData(id).then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); } } 复制代码
getDerivedStateFromProps
是一个静态方法,而组件实例没法继承静态方法,因此该生命周期钩子内部没法经过使用 this
获取组件实例的属性/方法。props
传递进这些方法中进行处理。
class
组件上,那么这些方法得申明成静态方法,而后在该生命周期钩子中经过 className.xxx
调用这些方法。class AAA extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { const data = AAA.filterFn(nextProps.data); return { data, prevId: nextProps.id, }; } return null; } static filterFn(data){ // 过滤数据 ... return newData; } ... } 复制代码
class
组件外面,就不用申明成静态方法,在该生命周期钩子中直接调用这些方法。function filterFn(data){ // 过滤数据 ... return newData; } class AAA extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { const data = filterFn(nextProps.data); return { data, prevId: nextProps.id, }; } return null; } ... } 复制代码
props
值。没法像组件实例的方法同样,能够在每一个组件实例方法内,经过 this.props.xxx / this.state.xxx
访问属性,会比较麻烦。componentDidUpdate
使用 在线 demoimport React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> <AAA /> </div> ); } class AAA extends React.Component { state = { age: 66 }; add = () => { this.setState({ age: this.state.age + 1 }); }; render() { return ( <div> <ChildA onChangeParent={this.add} age={this.state.age} /> </div> ); } } class ChildA extends React.Component { state = { num: 88 }; static getDerivedStateFromProps(nextprops, state) { console.log("getDerivedStateFromProps", nextprops); if (nextprops.age !== state.age) { return { // 给一个标识 status: false, // age: nextprops.age, onChangeParent: nextprops.onChangeParent }; } return null; } add = () => { this.setState({ num: this.state.num + 1 }); }; processData(){ console.log("process",this.props); return this.props.age; } componentDidUpdate() { // 根据标识来更新状态 if (!this.state.status) { this.setState({ age: this.processData(), status: true }); console.log("componentDidUpdate"); } } componentDidMount() { this.setState({ age: this.props.age, status: true }); } render() { const { onChangeParent } = this.state; console.log("render", this.state); return ( <> <div onClick={onChangeParent}>change</div> <div onClick={this.add}>add</div> </> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 复制代码
getDerivedStateFromProps
派生状态时,不须要把组件自身的状态也设置进去class AAA extends React.Component { // 必须给 state 设置一个值,哪怕是一个空对象 state = { num:666 }; static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { return { data:nextProps.data, prevId: nextProps.id, // 只须要映射属性,不须要把组件自身的状态也加进去 // num:prevState.num }; } return null; } ... } 复制代码
setState
更新的值不变,那么还会触发这些生命周期钩子吗?import React, {Component} from 'react' export default class LifeCycle extends Component { static defaultProps = { name: '计数器' }; constructor(props) { super(props); this.state = {number: 0};//初始化默认的状态对象 console.log('1. constructor 初始化 props and state'); } componentWillMount() { console.log('2. componentWillMount 组件将要挂载'); } componentDidMount() { console.log('4. componentDidMount 组件挂载完成'); } shouldComponentUpdate(nextProps, nextState) { console.log('Counter', nextProps, nextState); console.log('5. shouldComponentUpdate 询问组件是否须要更新'); return true; } componentWillUpdate() { console.log('6. componentWillUpdate 组件将要更新'); } componentDidUpdate() { console.log('7. componentDidUpdate 组件更新完毕'); } add = () => { this.setState({number: this.state.number }); }; render() { console.log('3.render渲染') return ( <div style={{border: '5px solid red', padding: '5px'}}> <p>{this.state.number}</p> <button onClick={this.add}>+</button> </div> ) } } 复制代码
componentWillMount
中添加事件监听componentDidMount
中添加事件监听componentWillMount
能够被打断或调用屡次,所以没法保证事件监听能在 unmount 的时候被成功卸载,可能会引发内存泄露dom
被挂载以前的阶段均可以被打断重来,致使 componentWillMount
、componentWillUpdate
、componentWillReceiveProps
在一次更新中可能会被触发屡次,所以那些只但愿触发一次的反作用应该放在 componentDidMount
中componentDidMount
中,而不是放在 componentWillMount
中的缘由,为了向后兼容getDerivedStateFromProps
和 componentWillReceiveProps
只会在 props
“改变”时才会调用。实际上只要父组件从新渲染时,这两个生命周期函数就会从新调用,无论 props
有没有“变化”React v16.9.0 and the Roadmap Updatehtml
你可能不须要使用派生 statereact
傻傻分不清之 Cookie、Session、Token、JWTgit
React Hooks 详解 【近 1W 字】+ 项目实战github