React生命周期與父子組件間通信知識點(diǎn)詳細(xì)講解
聲明周期
很多的事物都有從創(chuàng)建到銷毀的整個過程,這個過程稱之為是生命周期;
React組件也有自己的生命周期,了解組件的生命周期可以讓我們在最合適的地方完成自己想要的功能;
生命周期和生命周期函數(shù)的關(guān)系:
生命周期是一個抽象的概念,在生命周期的整個過程,分成了很多個階段;
比如裝載階段(Mount),組件第一次在DOM樹中被渲染的過程;
比如更新過程(Update),組件狀態(tài)發(fā)生變化,重新更新渲染的過程;
比如卸載過程(Unmount),組件從DOM樹中被移除的過程;
React內(nèi)部為了告訴我們當(dāng)前處于哪些階段,會對我們組件內(nèi)部實(shí)現(xiàn)的某些函數(shù)進(jìn)行回調(diào),這些函數(shù)就是生命周期函數(shù):
比如實(shí)現(xiàn)componentDidMount函數(shù):組件已經(jīng)掛載到DOM上時,就會回調(diào);
比如實(shí)現(xiàn)componentDidUpdate函數(shù):組件已經(jīng)發(fā)生了更新時,就會回調(diào);
比如實(shí)現(xiàn)componentWillUnmount函數(shù):組件即將被移除時,就會回調(diào);
我們可以在這些回調(diào)函數(shù)中編寫自己的邏輯代碼,來完成自己的需求功能;
我們談React生命周期時,主要談的類的生命周期,因?yàn)楹瘮?shù)式組件是沒有生命周期函數(shù)的
聲明周期解析
我們先來了解一下最基礎(chǔ)、最常用的生命周期函數(shù):
生命周期函數(shù)
class HelloWorld extends React.Component { // 1.構(gòu)造方法: constructor constructor() { console.log("HelloWorld constructor") super() this.state = { message: "Hello World" } } changeText() { this.setState({ message: "你好啊, 李銀河" }) } // 2.執(zhí)行render函數(shù) render() { console.log("HelloWorld render") const { message } = this.state return ( <div> <h2>{message}</h2> <p>{message}是程序員的第一個代碼!</p> <button onClick={e => this.changeText()}>修改文本</button> </div> ) } // 3.組件被渲染到DOM: 被掛載到DOM componentDidMount() { console.log("HelloWorld componentDidMount") } // 4.組件的DOM被更新完成: DOM發(fā)生更新 componentDidUpdate(prevProps, prevState, snapshot) { console.log("HelloWorld componentDidUpdate:", prevProps, prevState, snapshot) } // 5.組件從DOM中卸載掉: 從DOM移除掉 componentWillUnmount() { console.log("HelloWorld componentWillUnmount") } // 不常用的生命周期補(bǔ)充 shouldComponentUpdate() { return true } getSnapshotBeforeUpdate() { console.log("getSnapshotBeforeUpdate") return { scrollPosition: 1000 } } }
Constructor
如果不初始化 state 或不進(jìn)行方法綁定,則不需要為 React 組件實(shí)現(xiàn)構(gòu)造函數(shù)。
constructor中通常只做兩件事情:
通過給 this.state 賦值對象來初始化內(nèi)部的state;
為事件綁定實(shí)例(this);
componentDidMount
componentDidMount() 會在組件掛載后(插入 DOM 樹中)立即調(diào)用。
componentDidMount中通常進(jìn)行哪里操作呢?
依賴于DOM的操作可以在這里進(jìn)行;
在此處發(fā)送網(wǎng)絡(luò)請求就最好的地方;(官方建議)
可以在此處添加一些訂閱(會在componentWillUnmount取消訂閱);
componentDidUpdate
componentDidUpdate() 會在更新后會被立即調(diào)用,首次渲染不會執(zhí)行此方法。
當(dāng)組件更新后,可以在此處對 DOM 進(jìn)行操作;
如果你對更新前后的 props 進(jìn)行了比較,也可以選擇在此處進(jìn)行網(wǎng)絡(luò)請求;(例如,當(dāng) props 未發(fā)生變化時,則不會執(zhí)行網(wǎng)絡(luò)請求)。
componentWillUnmount
componentWillUnmount() 會在組件卸載及銷毀之前直接調(diào)用。
在此方法中執(zhí)行必要的清理操作;
例如,清除 timer,取消網(wǎng)絡(luò)請求或清除在 componentDidMount() 中創(chuàng)建的訂閱等;
不常用的生命周期函數(shù)
除了上面介紹的生命周期函數(shù)之外,還有一些不常用的生命周期函數(shù):
getDerivedStateFromProps:state 的值在任何時候都依賴于 props時使用;該方法返回一個對象來更新state;
getSnapshotBeforeUpdate:在React更新DOM之前回調(diào)的一個函數(shù),可以獲取DOM更新前的一些信息(比如說滾動位置);
shouldComponentUpdate:該生命周期函數(shù)很常用,但是我們等待講性能優(yōu)化時再來詳細(xì)講解;
另外,React中還提供了一些過期的生命周期函數(shù),這些函數(shù)已經(jīng)不推薦使用。
認(rèn)識組件間的通信
在開發(fā)過程中,我們會經(jīng)常遇到需要組件之間相互進(jìn)行通信:
比如App可能使用了多個Header,每個地方的Header展示的內(nèi)容不同,那么我們就需要使用者傳遞給Header一些數(shù)據(jù),讓其進(jìn)行展示;
又比如我們在Main中一次性請求了Banner數(shù)據(jù)和ProductList數(shù)據(jù),那么就需要傳遞給他們來進(jìn)行展示;
也可能是子組件中發(fā)生了事件,需要由父組件來完成某些操作,那就需要子組件向父組件傳遞事件;
總之,在一個React項(xiàng)目中,組件之間的通信是非常重要的環(huán)節(jié);
父組件在展示子組件,可能會傳遞一些數(shù)據(jù)給子組件:
父組件通過 屬性=值 的形式來傳遞給子組件數(shù)據(jù);
子組件通過 props 參數(shù)獲取父組件傳遞過來的數(shù)據(jù);
參數(shù)propTypes
對于傳遞給子組件的數(shù)據(jù),有時候我們可能希望進(jìn)行驗(yàn)證,特別是對于大型項(xiàng)目來說:
當(dāng)然,如果你項(xiàng)目中默認(rèn)繼承了Flow或者TypeScript,那么直接就可以進(jìn)行類型驗(yàn)證;
但是,即使我們沒有使用Flow或者TypeScript,也可以通過 prop-types 庫來進(jìn)行參數(shù)驗(yàn)證;
從 React v15.5 開始,React.PropTypes 已移入另一個包中:prop-types 庫
import PropTypes from 'prop-types'; MyComponent.propTypes = { // 你可以將屬性聲明為 JS 原生類型,默認(rèn)情況下 // 這些屬性都是可選的。 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, // 任何可被渲染的元素(包括數(shù)字、字符串、元素或數(shù)組) // (或 Fragment) 也包含這些類型。 optionalNode: PropTypes.node, // 一個 React 元素。 optionalElement: PropTypes.element, // 一個 React 元素類型(即,MyComponent)。 optionalElementType: PropTypes.elementType, // 你也可以聲明 prop 為類的實(shí)例,這里使用 // JS 的 instanceof 操作符。 optionalMessage: PropTypes.instanceOf(Message), // 你可以讓你的 prop 只能是特定的值,指定它為 // 枚舉類型。 optionalEnum: PropTypes.oneOf(['News', 'Photos']), // 一個對象可以是幾種類型中的任意一個類型 optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), // 可以指定一個數(shù)組由某一類型的元素組成 optionalArrayOf: PropTypes.arrayOf(PropTypes.number), // 可以指定一個對象由某一類型的值組成 optionalObjectOf: PropTypes.objectOf(PropTypes.number), // 可以指定一個對象由特定的類型值組成 optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), // An object with warnings on extra properties optionalObjectWithStrictShape: PropTypes.exact({ name: PropTypes.string, quantity: PropTypes.number }), // 你可以在任何 PropTypes 屬性后面加上 `isRequired` ,確保 // 這個 prop 沒有被提供時,會打印警告信息。 requiredFunc: PropTypes.func.isRequired, // 任意類型的必需數(shù)據(jù) requiredAny: PropTypes.any.isRequired, // 你可以指定一個自定義驗(yàn)證器。它在驗(yàn)證失敗時應(yīng)返回一個 Error 對象。 // 請不要使用 `console.warn` 或拋出異常,因?yàn)檫@在 `oneOfType` 中不會起作用。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, // 你也可以提供一個自定義的 `arrayOf` 或 `objectOf` 驗(yàn)證器。 // 它應(yīng)該在驗(yàn)證失敗時返回一個 Error 對象。 // 驗(yàn)證器將驗(yàn)證數(shù)組或?qū)ο笾械拿總€值。驗(yàn)證器的前兩個參數(shù) // 第一個是數(shù)組或?qū)ο蟊旧? // 第二個是他們當(dāng)前的鍵。 customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
限制單個元素
你可以通過 PropTypes.element 來確保傳遞給組件的 children 中只包含一個元素。
import PropTypes from 'prop-types'; class MyComponent extends React.Component { render() { // 這必須只有一個元素,否則控制臺會打印警告。 const children = this.props.children; return ( <div> {children} </div> ); } } MyComponent.propTypes = { children: PropTypes.element.isRequired };
默認(rèn) Prop 值
您可以通過配置特定的 defaultProps 屬性來定義 props 的默認(rèn)值:
class Greeting extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } // 指定 props 的默認(rèn)值: Greeting.defaultProps = { name: 'Stranger' }; // 渲染出 "Hello, Stranger": const root = ReactDOM.createRoot(document.getElementById('example')); root.render(<Greeting />);
從 ES2022 開始,你也可以在 React 類組件中將 defaultProps 聲明為靜態(tài)屬性。這種現(xiàn)代語法需要添加額外的編譯步驟才能在老版瀏覽器中工作。
class Greeting extends React.Component { static defaultProps = { name: 'stranger' } render() { return ( <div>Hello, {this.props.name}</div> ) } }
defaultProps 用于確保 this.props.name 在父組件沒有指定其值時,有一個默認(rèn)值。propTypes 類型檢查發(fā)生在 defaultProps 賦值后,所以類型檢查也適用于 defaultProps
對于函數(shù)式組件
如果你在常規(guī)開發(fā)中使用函數(shù)組件,那你可能需要做一些適當(dāng)?shù)母膭?,以保證 PropsTypes 應(yīng)用正常。
假設(shè)你有如下組件:
export default function HelloWorldComponent({ name }) { return ( <div>Hello, {name}</div> ) }
如果要添加 PropTypes,你可能需要在導(dǎo)出之前以單獨(dú)聲明的一個函數(shù)的形式,聲明該組件,具體代碼如下:
function HelloWorldComponent({ name }) { return ( <div>Hello, {name}</div> ) } export default HelloWorldComponent
接著,可以直接在 HelloWorldComponent 上添加 PropTypes:
import PropTypes from 'prop-types' function HelloWorldComponent({ name }) { return ( <div>Hello, {name}</div> ) } HelloWorldComponent.propTypes = { name: PropTypes.string } export default HelloWorldComponent
子組件傳遞父組件
某些情況,我們也需要子組件向父組件傳遞消息:
在vue中是通過自定義事件來完成的;
在React中同樣是通過props傳遞消息,只是讓父組件給子組件傳遞一個回調(diào)函數(shù),在子組件中調(diào)用這個函數(shù)即可;
我們這里來完成一個案例:
將計數(shù)器案例進(jìn)行拆解;
將按鈕封裝到子組件中:CounterButton;
CounterButton發(fā)生點(diǎn)擊事件,將內(nèi)容傳遞到父組件中,修改counter的值;
app.jsx
import React, { Component } from 'react' import AddCounter from './AddCounter' import SubCounter from './SubCounter' export class App extends Component { constructor() { super() this.state = { counter: 100 } } changeCounter(count) { this.setState({ counter: this.state.counter + count }) } render() { const { counter } = this.state return ( <div> <h2>當(dāng)前計數(shù): {counter}</h2> <AddCounter addClick={(count) => this.changeCounter(count)}/> <SubCounter subClick={(count) => this.changeCounter(count)}/> </div> ) } } export default App
subCounter.jsx
import React, { Component } from 'react' export class SubCounter extends Component { subCount(count) { this.props.subClick(count) } render() { return ( <div> <button onClick={e => this.subCount(-1)}>-1</button> <button onClick={e => this.subCount(-5)}>-5</button> <button onClick={e => this.subCount(-10)}>-10</button> </div> ) } } export default SubCounter
addCounter.jsx
import React, { Component } from 'react' // import PropTypes from "prop-types" export class AddCounter extends Component { addCount(count) { this.props.addClick(count) } render() { return ( <div> <button onClick={e => this.addCount(1)}>+1</button> <button onClick={e => this.addCount(5)}>+5</button> <button onClick={e => this.addCount(10)}>+10</button> </div> ) } } // AddCounter.propTypes = { // addClick: PropTypes.func // } export default AddCounter
到此這篇關(guān)于React生命周期與父子組件間通信知識點(diǎn)詳細(xì)講解的文章就介紹到這了,更多相關(guān)React生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react項(xiàng)目中redux的調(diào)試工具不起作用的解決
這篇文章主要介紹了react項(xiàng)目中redux的調(diào)試工具不起作用的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01阿里低代碼框架lowcode-engine自定義設(shè)置器詳解
這篇文章主要為大家介紹了阿里低代碼框架lowcode-engine自定義設(shè)置器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解
這篇文章主要為大家介紹了詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04react性能優(yōu)化useMemo與useCallback使用對比詳解
這篇文章主要為大家介紹了react性能優(yōu)化useMemo與useCallback使用對比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08React?跨端動態(tài)化核心技術(shù)實(shí)例分析
這篇文章主要為大家介紹了React?跨端動態(tài)化核心技術(shù)實(shí)例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10