React組件間通信

組件間不同的嵌套關(guān)系,會(huì)導(dǎo)致不同的通信方式。常見的有:父組件向子組件通信、子組件向父組件通信、沒有嵌套關(guān)系的組件之間的通信,還有一種特殊形式:跨級(jí)組件通信。

1、父組件向子組件通信

這是React中最為常見的一種通信方式,父組件通過props向子組件傳遞需要的信息。示例如下:

class Child extends Component{
    render(){
        const { name } = this.props;
        return <p>hello, { name }</p>;
    }
}

class Parent extends Component{
    render(){
        return (
            <div>
                <Child name='Bob' />
            </div>
        );
    }
}

2、子組件向父組件通信

子組件向父組件通信有兩種方式

  • 利用回調(diào)函數(shù)
  • 利用自定義事件機(jī)制

相較而言回調(diào)函數(shù)更為簡單,一般多用這種方式。其原理為:父組件將一個(gè)函數(shù)作為props傳遞給子組件,子組件調(diào)用這個(gè)回調(diào)函數(shù),將想要傳遞的信息,作為參數(shù),傳遞給父組件。示例如下:

class Parent extends Component{
    constructor(props){
        super(props);
        this.handleClick = this.handleClick.bind(this);
        this.state={
            visible: false。
        }
    }

    handleClick(){
        this.setState({
            visible: true,
        });
    }

    render(){
        return (
            <React.Fragment>
                <div style={{display: this.state.visible ? 'block' : 'none'}}>
                    我是被隱藏的文字
                </div>
                <Child name='Bob' handleClick={this.handleClick} />
            </React.Fragment>
        );
    }
}
class Child extends Component{
    render(){
        const { handleClick } = this.props;
        return (<button onClick={handleClick}>點(diǎn)擊顯示隱藏的文字</button>);
    }
}

3、跨級(jí)組件通信

跨級(jí)組件通信有兩種方法:(1)向?qū)訉觽鬟fprops。(2)利用context。

對(duì)于第一種方式,如果組件結(jié)構(gòu)較深,那么中間每一層都需要傳遞props,增加了復(fù)雜度且造成了冗余。而context 相當(dāng)于一個(gè)全局變量,是一個(gè)大容器,我們可以把要通信的內(nèi)容放在這個(gè)容器中,這樣一來,不管嵌套有多深,都可以隨意取用。使用 context 也很簡單,需要滿足兩個(gè)條件:

  • 上級(jí)組件要聲明自己支持 context,并提供一個(gè)函數(shù)來返回相應(yīng)的 context 對(duì)象
  • 子組件要聲明自己需要使用 context

示例:

export default class GrandParent extends Component{
    // 父組件聲明自己支持 context
    static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    // 父組件提供一個(gè)函數(shù),用來返回相應(yīng)的 context 對(duì)象
    getChildContext(){
        return{
            color:"red",
            callback:this.callback.bind(this)
        }
    }

    callback(msg){
        console.log(msg)
    }

    render(){
        return(
            <div>
                <Parent></Parent>
            </div>
        );
    }
}


const Parent = (props) =>{
    return(
        <div>
            <Child />
        </div>
    );
}


export default class Child extends Component{
    // 子組件聲明自己需要使用 context
    static contextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    render(){
        const style = { color:this.context.color }
        const handleConsoleLog = (msg) => {
            return () => {
                this.context.callback(msg);
            }
        }

        return(
            <div style = { style }>
                Child組件
                <button onClick = { handleConsoleLog("我胡漢三又回來了!") }>點(diǎn)擊我</button>
            </div>
        );
    }
}

總結(jié):如果是父組件向子組件單向通信,可以使用變量,如果子組件想向父組件通信,同樣可以由父組件提供一個(gè)回調(diào)函數(shù),供子組件調(diào)用,回傳參數(shù)。

注意:如果組件中使用構(gòu)造函數(shù)(constructor),還需要在構(gòu)造函數(shù)中傳入第二個(gè)參數(shù) context,并在 super 調(diào)用父類構(gòu)造函數(shù)是傳入 context,否則會(huì)造成組件中無法使用 context。

constructor(props,context){
  super(props,context);
}

Context就像全局變量一樣,而全局變量正是導(dǎo)致應(yīng)用走向混亂的罪魁禍?zhǔn)字?,給組件帶來了外部依賴的副作用,因此,不推薦使用context。其比較好的應(yīng)用場(chǎng)景是:真正意義上的全局信息且不會(huì)更改,如界面主題,用戶信息??傮w原則是:如果真的需要使用,建議寫成高階組件來實(shí)現(xiàn)。

補(bǔ)充

1、context對(duì)象的更改。

我們不應(yīng)該也不能直接改變context對(duì)象中的屬性。要想改變 context 對(duì)象,只有讓其和父組件的 state 或者 props 進(jìn)行關(guān)聯(lián),在父組件的 state 或 props 變化時(shí),會(huì)自動(dòng)調(diào)用 getChildContext 方法,返回新的 context 對(duì)象,而后子組件進(jìn)行相應(yīng)的渲染。

    constructor(props) {
        super(props);
        this.state = {
            color:"red"
        };
    }
    // 父組件聲明自己支持 context
    static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    // 父組件提供一個(gè)函數(shù),用來返回相應(yīng)的 context 對(duì)象
    getChildContext(){
        return{
            color:this.state.color,
            callback:this.callback.bind(this)
        }
    }

2、context同樣可以引用在無狀態(tài)組件上,只需將context作為第二個(gè)參數(shù)即可。

const Child = (props,context) => {
    const style = { color:context.color }
    const handleConsoleLog = (msg) => {
        return () => {
            context.callback(msg);
        }
    }

    return(
        <div style = { style }>
            Child組件
            <button onClick = { handleConsoleLog("我胡漢三又回來了!") }>點(diǎn)擊我</button>
        </div>
    );
}

Child.contextTypes = {
    color:PropTypes.string,
    callback:PropTypes.func,
}

4、沒有嵌套關(guān)系的組件通信

沒有嵌套關(guān)系的組件通信包括兄弟組件通信和不在同一個(gè)父級(jí)中的非兄弟組件。同樣有兩種通信方式:

  • 利用二者共同父組件的context對(duì)象進(jìn)行通信
  • 利用自定義事件

第一種方法利用父組件中轉(zhuǎn),會(huì)增加子組件和父組件之間的耦合度,如果組件層次較深,找到二者公共父組件不太容易。一般使用自定義事件實(shí)現(xiàn)。

自定義事件需要借用node.js的events模塊:

安裝:npm install events --save
引入:import { EventEmitter } from "events";
     export default new EventEmitter(); // 初始化實(shí)例并輸出給其他組件使用
export default class App extends Component{
    render(){
        return(
            <div>
                <Foo />
                <Boo />
            </div>
        );
    }
}


export default class Foo extends Component{
    constructor(props) {
        super(props);
        this.state = {
            msg:null,
        };
    }
    componentDidMount(){
        // 聲明一個(gè)自定義事件
        this.eventEmitter = emitter.on("callMe",(msg)=>{
            this.setState({
                msg,
            })
        });
    }
    // 組件銷毀前移除事件監(jiān)聽
    componentWillUnmount(){
        emitter.removeListener(this.eventEmitter);
    }
    render(){
        return(
            <div>
                { this.state.msg }
                我是非嵌套 1 號(hào)
            </div>
        );
    }
}


export default class Boo extends Component{
    render(){
        const cb = (msg) => {
            return () => {
                // 觸發(fā)自定義事件。參一為事件名,后面為傳遞給事件的參數(shù),可多個(gè)。
                emitter.emit("callMe","Hello")
            }
        }
        return(
            <div>
                我是非嵌套 2 號(hào)
                <button onClick = { cb("blue") }>點(diǎn)擊我</button>
            </div>
        );
    }
}

5、總結(jié):

幾種通信情況下,最適用的方式:

  • 父組件向子組件通信:使用 props
  • 子組件向父組件通信:使用 props 回調(diào)
  • 跨級(jí)組件間通信:使用 context 對(duì)象
  • 非嵌套組件間通信:使用事件訂閱
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容