1. 程式人生 > 其它 >React 中元件之間的通訊方式

React 中元件之間的通訊方式

元件間通訊方式

在使用 React 的過程中,經常需要元件之間相互傳遞資訊,故記錄一下元件間的常用通訊方式:

  1. 父元件向子元件通訊
    父元件向子元件傳遞 props, 子元件通過獲取 props 中的內容得到父元件傳遞的資訊;
    示例:父元件(App)向子元件(Root)通訊

    import Root from "./components/Root";
    
    const App = (props) => {
      return (
    	<div className="app">
    	  {/* 
    		向自定義元件(Root)設定(msg)屬性;
    		自定義元件(Root)會將所接收到的屬性(attributes)和子元件(children)
    		轉換為單個物件(props)傳遞給元件;
    	   */}
    	  <Root msg="this is a test message" />
    	</div>
      );
    }
    
    // 父元件 App
    export default App;
    
    const Root = (props) => {
    	// 獲取父元件傳遞的msg屬性值
    	const msg = props.msg;
    	return (
    		<div className="root">
    			{msg}  {/* this is a test message */}
    		</div>
    	);
    }
    
    // 子元件 Root
    export default Root;
    
  2. 子元件向父元件通訊
    父元件向子元件傳遞一個回撥函式屬性, 子元件通過呼叫父元件傳遞的回撥函式實現子元件向父元件通訊;
    示例:子元件(Root)向父元件(App)通訊

    import Root from "./components/Root";
    
    const App = (props) => {
      const getName = (name) => {
    	// my name is root
    	console.log(name);
      }
      return (
    	<div className="app">
    	  {/*向子元件傳遞callback屬性,屬性值為一個回撥函式 */}
    	  <Root callback={getName} />
    	</div>
      );
    }
    
    // 父元件 App
    export default App;
    
    const Root = (props) => {
    	// 獲取父元件傳遞的callback屬性值(getName函式)
    	const getName = props.callback;
    	const name = "my name is Root";
    	// 呼叫getName函式
    	getName && getName(name);
    	return null;
    }
    
    // 子元件 Root
    export default Root;
    
  3. 跨級元件間通訊
    跨級元件是指父元件的子元件的子元件,或者更深層的巢狀關係,跨級元件之間想要通訊,有兩種常見方式:

    1. 中間元件層層傳遞
    2. 使用 Context
      物件

    示例:頂層元件 App 向"孫子"級元件 Content 通訊

    import React from "react";
    import Root from "./components/Root";
    
    const App = (props) => {
      const context = {
    	name: "趙雲"
      }
      return (
    	// Provider(生產者)共享容器,在頂層傳入value
    	<App.Context.Provider value={context}>
    	  <div className="app">
    		<Root />
    	  </div>
    	</App.Context.Provider>
      );
    }
    
    // 建立一個Context
    App.Context = React.createContext();
    
    // 頂層元件 App
    export default App;
    
    import Content from "./Content";
    
    const Root = (props) => {
    	return (
    		// 中間元件
    		<Content />
    	);
    }
    
    // 中間元件 Root
    export default Root;
    
    import App from '../App';
    
    const Content = (props) => {
    	return (
    		// Consumer(消費者)容器,可以獲取從頂層傳遞的context
    		<App.Context.Consumer>
    			{/* 以函式的方式使用context */}
    			{context => (
    				<div className='content'>
    					{context.name}  {/* 趙雲 */}
    				</div>
    			)}
    		</App.Context.Consumer>
    	);
    }
    
    // 目標通訊元件 Content
    export default Content;
    
  4. 非巢狀元件間通訊
    非巢狀元件,就是通訊元件間沒有任何包含關係,包括兄弟元件以及不在同一個父級中的非兄弟元件;對於非巢狀元件,可以利用這兩種方式通訊:

    1. 利用二者共同父元件的 context 物件進行通訊
    2. 使用自定義事件的方式
      如果採用元件間共同的父級來進行中轉,會增加子元件和父元件之間的耦合度,如果元件層次較深的話,找到二者公共的父元件不是一件容易的事;

    使用自定義事件的方式來實現非巢狀元件間的通訊。

    示例:Root 元件向 Content 元件通訊

    # 這裡需要一個 events 依賴來做釋出訂閱動作
    npm install events --save
    
    import React from "react";
    import Root from "./components/Root";
    import Content from "./components/Content";
    
    const App = (props) => {
      return (
    	  <div className="app">
    		<Root />
    		<Content />
    	  </div>
      );
    }
    
    // 頂層元件 App
    export default App;
    
    import React from 'react';
    import emitter from "./event";
    
    const Root = (props) => {
    	// 初始化 state, 相當於 class 元件中的 this.state = {rootName: ""}
    	const [rootName, setRootName] = React.useState("");
    
    	// 監聽 setRootName 事件的回撥函式 
    	const setRootNameCallback = (name) => {
    		setRootName(name);
    	}
    
    	// 類似於類元件中的 componentDidMount
    	React.useEffect(() => {
    		emitter.addListener("setRootName", setRootNameCallback);
    	}, []);
    
    	// 類似於類組價中 componentWillUnmout
    	React.useEffect(() => {
    		// 元件解除安裝移除監聽
    		return () => {
    			emitter.removeListener("setRootName", setRootNameCallback);
    		}
    	});
    
    	return (
    		// 兄弟元件
    		<div className="root">
    			{rootName}  {/* 趙雲 */}
    		</div>
    	);
    }
    
    // 兄弟元件 Root
    export default Root;
    
    import React from "react";
    import emitter from "./event";
    
    const Content = (props) => {
    
    	// 類似於類元件中的 componentDidMount
    	React.useEffect(() => {
    		// 觸發 setRootName 事件,向訂閱者傳值;
    		emitter.emit("setRootName", "趙雲");
    	}, []);
    
    	return (
    		// 兄弟元件
    		<div className="content">
    
    		</div>
    	);
    }
    
    // 兄弟元件 Content
    export default Content;
    

總結

本文記錄了不同巢狀關係的元件間常用的幾種通訊方式:

  1. 父子巢狀關係:利用 props 物件實現父元件向子元件通訊;
  2. 父子巢狀關係:利用 callback(回撥函式) 實現子元件向父元件通訊;
  3. 多層(父子)巢狀關係(跨級通訊):利用 Context 物件, 以生產者和消費者的方式實現通訊;
  4. 非巢狀關係:利用 events (釋出訂閱) 的方式實現通訊;

如果專案比較大,巢狀關係比較複雜,state 就會變得難以管理,就需要考慮使用 Redux 庫管理狀態;

參考學習部落格

React 中元件間通訊的幾種方式