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
在一次更新中可能會被觸發屢次,所以那些只但願觸發一次的反作用應該放在 componentDidUpdate
中componentDidMount
中,而不是放在 componentWillMount
中的緣由,爲了向後兼容getDerivedStateFromProps
和 componentWillReceiveProps
只會在 props
「改變」時纔會調用。實際上只要父組件從新渲染時,這兩個生命週期函數就會從新調用,無論 props
有沒有「變化」React v16.9.0 and the Roadmap Updatehtml
你可能不須要使用派生 statereact
React Hooks 詳解 【近 1W 字】+ 項目實戰git