React 中组件之间的通信方式
组件间通信方式
在使用 React 的过程中,经常需要组件之间相互传递信息,故记录一下组件间的常用通信方式:
-
父组件向子组件通信
父组件向子组件传递 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;
-
子组件向父组件通信
父组件向子组件传递一个回调函数属性, 子组件通过调用父组件传递的回调函数实现子组件向父组件通信;
示例:子组件(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;
-
跨级组件间通信
跨级组件是指父组件的子组件的子组件,或者更深层的嵌套关系,跨级组件之间想要通信,有两种常见方式:- 中间组件层层传递
- 使用 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;
-
非嵌套组件间通信
非嵌套组件,就是通信组件间没有任何包含关系,包括兄弟组件以及不在同一个父级中的非兄弟组件;对于非嵌套组件,可以利用这两种方式通信:- 利用二者共同父组件的 context 对象进行通信
- 使用自定义事件的方式
如果采用组件间共同的父级来进行中转,会增加子组件和父组件之间的耦合度,如果组件层次较深的话,找到二者公共的父组件不是一件容易的事;
使用自定义事件的方式来实现非嵌套组件间的通信。
示例: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;
总结
本文记录了不同嵌套关系的组件间常用的几种通信方式:
- 父子嵌套关系:利用 props 对象实现父组件向子组件通信;
- 父子嵌套关系:利用 callback(回调函数) 实现子组件向父组件通信;
- 多层(父子)嵌套关系(跨级通信):利用 Context 对象, 以生产者和消费者的方式实现通信;
- 非嵌套关系:利用 events (发布订阅) 的方式实现通信;
如果项目比较大,嵌套关系比较复杂,state 就会变得难以管理,就需要考虑使用 Redux 库管理状态;