组件通信的方式有哪些
-
⽗组件向⼦组件通讯: ⽗组件可以向⼦组件通过传 props 的⽅式,向⼦组件进⾏通讯
-
⼦组件向⽗组件通讯: props+回调的⽅式,⽗组件向⼦组件传递props进⾏通讯,此props为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中
-
兄弟组件通信: 找到这两个兄弟节点共同的⽗节点,结合上⾯两种⽅式由⽗节点转发信息进⾏通信
-
跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过
-
发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信
-
全局状态管理⼯具: 借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态
父子组件通信
父组件向子组件通信:父组件传递数据->子组件标签上绑定属性,子组件接收数据->props的参数
- 父组件提供要传递的数据
- 给子组件标签添加属性,属性值为我们要传送的数据
- 子组件通过props接收父组件中传递的数据
// 父组件
function Father(){
const name = 'test'
return(
<div>
<Child name={name}/>
</div>
)
}
// 子组件
function Child(props){
return (
<div>
{props.name}
</div>
)
}
子组件向父组件通信:在子组件中调用父组件中的函数并传递参数,【利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数】
- 子组件去传值,通过触发父组件的方法,父组件的方法通过props传递到子组件
- 父组件自定义子组件中的方法
- 子组件调用父组件中的自定义方法
// 父组件
function Father(){
const [msg,setMsg] = useState('')
const getMsg = (msg)=>{
console.log(msg)
setMsg(msg)
}
return(
<div>
{msg}
<Child onGetSonMsg={getMsg}/>
</div>
)
}
// 子组件
function Child({onGetSonMsg}){
const sonMsg = 'son msg'
return (
<div>
<button onClick={()=>onGetSonMsg(sonMsg)}></button>
</div>
)
}
兄弟组件通信:
1、利用共同父组件通信1:通过子传父(A-> Father)2:通过父传子(Father->B)
// 父组件
import React, { useState } from 'react';
import BrotherOne from './BrotherOne';
import BrotherTwo from './BrotherTwo';
const Parent = () => {
const [sharedData, setSharedData] = useState("");
const handleData = (data) => {
setSharedData(data);
};
return (
<div>
<h1>Parent Component</h1>
<BrotherOne sendData={handleData} />
<BrotherTwo receivedData={sharedData} />
</div>
);
};
export default Parent;
// 兄弟组件One
import React from 'react';
const BrotherOne = ({ sendData }) => {
const sendMessage = () => {
sendData("Hello from Brother One");
};
return (
<div>
<h2>Brother One</h2>
<button onClick={sendMessage}>Send Message to Brother Two</button>
</div>
);
};
export default BrotherOne;
// 兄弟组件Two
import React from 'react';
const BrotherTwo = ({ receivedData }) => {
return (
<div>
<h2>Brother Two</h2>
<p>Received: {receivedData}</p>
</div>
);
};
export default BrotherTwo;
2、使用Event Bus通信
使用事件总线(Event Bus)来进行兄弟组件间的通信。事件总线可以是一个简单的发布/订阅模式实现,允许组件之间通过事件来传递数据。
// EventBus.js
import { EventEmitter } from 'events';
const eventBus = new EventEmitter();
export default eventBus;
// 兄弟组件One
import React from 'react';
import eventBus from './EventBus';
const BrotherOne = () => {
const sendMessage = () => {
eventBus.emit('message', 'Hello from Brother One');
};
return (
<div>
<h2>Brother One</h2>
<button onClick={sendMessage}>Send Message</button>
</div>
);
};
export default BrotherOne;
// 兄弟组件Two
import React, { useEffect, useState } from 'react';
import eventBus from './EventBus';
const BrotherTwo = () => {
const [message, setMessage] = useState('');
useEffect(() => {
const handleMessage = (msg) => {
setMessage(msg);
};
eventBus.on('message', handleMessage);
return () => {
eventBus.off('message', handleMessage);
};
}, []);
return (
<div>
<h2>Brother Two</h2>
<p>Received Message: {message}</p>
</div>
);
};
export default BrotherTwo;
解析:
通过引入一个事件总线EventBus,BrotherOne可以通过eventBus.emit发布消息,BrotherTwo通过eventBus.on监听消息,实现了无需共同父组件的兄弟组件间通信。这种方法适用于组件层级较深或彼此关系较远的场景。
跨级组件通信:Father->A->B
1、Context API
// ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext('light');
export default ThemeContext;
// 父组件
import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import DeepChild from './DeepChild';
const Parent = () => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={theme}>
<div>
<h1>Parent Component</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
<DeepChild />
</div>
</ThemeContext.Provider>
);
};
export default Parent;
// 深层子组件
import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';
const DeepChild = () => {
const theme = useContext(ThemeContext);
return (
<div>
<h2>Deep Child Component</h2>
<p>Current Theme: {theme}</p>
</div>
);
};
export default DeepChild;
通过ThemeContext.Provider将theme值提供给整个组件树,DeepChild组件通过useContext钩子直接获取到theme值,无需通过中间层级组件传递props,实现了跨层级的通信。
2、Redux状态管理
// store.js
import { createStore } from 'redux';
const initialState = {
message: ''
};
const reducer = (state = initialState, action) => {
switch(action.type) {
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
};
const store = createStore(reducer);
export default store;
// 父组件
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import BrotherOne from './BrotherOne';
import BrotherTwo from './BrotherTwo';
const Parent = () => {
return (
<Provider store={store}>
<div>
<h1>Parent Component</h1>
<BrotherOne />
<BrotherTwo />
</div>
</Provider>
);
};
export default Parent;
// 兄弟组件One
import React from 'react';
import { useDispatch } from 'react-redux';
const BrotherOne = () => {
const dispatch = useDispatch();
const sendMessage = () => {
dispatch({ type: 'SET_MESSAGE', payload: 'Hello from Brother One' });
};
return (
<div>
<h2>Brother One</h2>
<button onClick={sendMessage}>Send Message</button>
</div>
);
};
export default BrotherOne;
// 兄弟组件Two
import React from 'react';
import { useSelector } from 'react-redux';
const BrotherTwo = () => {
const message = useSelector(state => state.message);
return (
<div>
<h2>Brother Two</h2>
<p>Received Message: {message}</p>
</div>
);
};
export default BrotherTwo;
通过Redux的Provider将Store提供给整个应用,BrotherOne组件通过useDispatch发送动作来更新Store,BrotherTwo组件通过useSelector获取Store中的数据,实现了全局状态的共享和更新。
3、使用React hooks
// GlobalState.js
import React, { useReducer, createContext } from 'react';
const initialState = { message: '' };
const GlobalStateContext = createContext(initialState);
const GlobalDispatchContext = createContext(() => {});
const reducer = (state, action) => {
switch(action.type) {
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
};
const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<GlobalDispatchContext.Provider value={dispatch}>
<GlobalStateContext.Provider value={state}>
{children}
</GlobalStateContext.Provider>
</GlobalDispatchContext.Provider>
);
};
export { GlobalProvider, GlobalStateContext, GlobalDispatchContext };
// 父组件
import React from 'react';
import { GlobalProvider } from './GlobalState';
import BrotherOne from './BrotherOne';
import BrotherTwo from './BrotherTwo';
const Parent = () => {
return (
<GlobalProvider>
<div>
<h1>Parent Component</h1>
<BrotherOne />
<BrotherTwo />
</div>
</GlobalProvider>
);
};
export default Parent;
// 兄弟组件One
import React, { useContext } from 'react';
import { GlobalDispatchContext } from './GlobalState';
const BrotherOne = () => {
const dispatch = useContext(GlobalDispatchContext);
const sendMessage = () => {
dispatch({ type: 'SET_MESSAGE', payload: 'Hello from Brother One' });
};
return (
<div>
<h2>Brother One</h2>
<button onClick={sendMessage}>Send Message</button>
</div>
);
};
export default BrotherOne;
// 兄弟组件Two
import React, { useContext } from 'react';
import { GlobalStateContext } from './GlobalState';
const BrotherTwo = () => {
const { message } = useContext(GlobalStateContext);
return (
<div>
<h2>Brother Two</h2>
<p>Received Message: {message}</p>
</div>
);
};
export default BrotherTwo;
通过组合useReducer和useContext, GlobalProvider提供了一个全局状态和调度函数。BrotherOne通过useContext获取到dispatch来更新状态,BrotherTwo通过useContext获取到全局状态,实现了跨层级组件的通信。这种方式较为灵活,适合中小型项目。
解决props层级过深的问题
- 使用Context API:提供一种组件之间的状态共享,而不必通过显式组件树逐层传递props;
- 使用Redux等状态库。
Props的不可变性
props 不可变指的是,一旦父组件把 props 传递给子组件,子组件就不能直接去改变这些 props。
为什么React要遵循Props的不可变性?
- 单向数据流:React采用单向数据流架构,数据的流动是单向且可预测的。
- 数据一致性:不可变的props保证数据在不同组件之间的一致性。所有使用相同props的组件都会看到相同的数据,不会因为某个组件修改props导致其他组件的数据不一致
- 浅比较优化:React通过比较新旧props决定是否需要重新渲染组件。如果
props是不可变的,React 可以使用浅比较(只比较对象的引用)来快速判断props是否发生了变化。如果props没有变化,React 可以跳过组件的重新渲染,从而提高性能。 - 状态管理库:许多React状态管理库都依赖于数据的不可变性。
React如何保证props的不可变性?
- 不直接修改传入props对象,把props当作只读数据使用
- 当props是对象或者数组时,若要更新数据,不直接修改原始对象或数组,而是创建新的对象或数组。
若需要修改props的值,怎么解决?
- 第一种:让父组件来管理状态,子组件通过回调函数通知父组件更新状态。
import React, { useState } from 'react';
// 子组件,接收 value 和 onChange 作为 props
const ChildComponent = (props) => {
// 定义一个处理点击事件的函数
const handleClick = () => {
// 调用父组件传递过来的 onChange 函数,通知父组件更新值
props.onChange(props.value + 1);
};
return (
<div>
{/* 渲染 props 的值 */}
<p>{props.value}</p>
{/* 点击按钮触发 handleClick 函数 */}
<button onClick={handleClick}>增加</button>
</div>
);
};
// 父组件,负责管理状态
const ParentComponent = () => {
// 使用 useState 钩子来管理状态
const [value, setValue] = useState(0);
// 定义一个更新值的函数
const updateValue = (newValue) => {
// 调用 setValue 函数更新状态
setValue(newValue);
};
return (
<div>
{/* 将 value 和 updateValue 作为 props 传递给 ChildComponent */}
<ChildComponent value={value} onChange={updateValue} />
</div>
);
};
export default ParentComponent;
父组件 ParentComponent 用 useState 钩子来管理状态。子组件 ChildComponent 接收 value 和 onChange 作为 props。