1.父子组件传参:props
2.祖先和后代的传参:基于context
React Context 使用:可以通过Provider组件的value来传递数据,也可以通过调用React.createContext()来产生context,然后在Consumer组件获得context中的数据。
- 创建一个上下文对象
const ThemeContext = React.createContext();
export default ThemeContext;
- 使用上下文对象
(1)在祖先组件中使用 <ThemeContext.Provider>...</ThemeContext.Provider> 将后代元素包裹起来,并传递参数:
// 祖先组件
<ThemeContext.Provider
value={{
supNum,
oppNum,
change
}}>
<div className="vote-box">
<div className="header">
<h2 className="title">React是很棒的前端框架</h2>
<span className="num">{supNum + oppNum}</span>
</div>
<VoteMain />
<VoteFooter />
</div>
</ThemeContext.Provider>
(2)在后代元素中使用 <ThemeContext.Consumer>...</ThemeContext.Consumer> 将内部元素包裹起来,获取参数:
const VoteMain = function VoteMain() {
return <ThemeContext.Consumer>
{context => {
let { supNum, oppNum } = context;
return <div className="main">
<p>支持人数:{supNum}人</p>
<p>反对人数:{oppNum}人</p>
</div>;
}}
</ThemeContext.Consumer>;
};
对于函数组件,后代元素也可以使用 useContext 获取祖先组件传递的参数:
const VoteFooter = function VoteFooter() {
let { change } = useContext(ThemeContext);
return <div className="footer">
<Button type="primary" onClick={change.bind(null, 'sup')}>支持</Button>
<Button type="primary" danger onClick={change.bind(null, 'opp')}>反对</Button>
</div>;
};
context 的优缺点:
优点:改变了props需要一层层往下传递的问题,能够让数据在组件树中传递。基于树形结构共享数据的方式,在某个节点组件开启提供context后,所有后代节点组件都可以获取到共享的数据。
缺点:
- (1)context相当于全局变量, 难以追溯数据源
- (2)耦合度高,即不利于组件复用也不利于测试
- (3)当 Provider 的
value值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于React.memo和shouldComponentUpdate函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新
作者:CS_Joe
链接:juejin.cn/post/684490…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.不同页面之间传参:EventEmitter
手写一个 EventEmitter:
class EventEmitter {
eventListeners = {};
// 监听事件
on(event, cb) {
if (!this.eventListeners[event]) {
this.eventListeners[event] = [];
}
this.eventListeners[event].push(cb);
}
// 触发事件
once(event, cb) {
const onceCb = (...args) => {
cb(...args);
this.remove(event, onceCb);
};
this.on(event, onceCb);
}
emit(event, ...args) {
const cbs = this.eventListeners[event];
if (Array.isArray(cbs) && cbs.length) {
cbs.forEach((cb) => {
cb(...args);
});
}
}
remove(event, cb = null) {
let listeners = this.eventListeners[event];
if (!listeners) return;
if (cb) {
listeners = listeners.filter((listener) => listener === cb);
this.eventListeners[event] = listeners;
}
delete this.eventListeners[event];
}
}
4.Redux / mobx
Redux 原理:
- reducer 修改状态
- subscribe 订阅更新组件的事件
- dispatch 里调用reducer修改状态,同时触发所有subscribe订阅的事件,更新组件
interface ActionType {
type: any;
[k: string]: any;
}
export const createStore = (
reducer: (state: any, action: ActionType) => object
) => {
let state: object = reducer(undefined, { type: undefined });
const listeners: Array<() => void> = [];
const getState = () => {
return state;
};
const subscribe = (fn: () => void) => {
if (!listeners.includes(fn)) {
listeners.push(fn);
}
// 返回 unsubscribe 函数,移除这次 push 的 fn
return () => {
const index = listeners.indexOf(fn);
listeners.splice(index, 1);
};
};
const dispatch = (action: ActionType) => {
listeners.forEach((fn) => fn());
state = reducer(state, action);
return action;
};
return {
getState,
subscribe,
dispatch,
};
};
mobx 设计思想:
利用 defineProperty 做数据劫持
Redux 和 mobx 区别:
(1)store 的区别:redux将所有共享的应用数据放在一个大的store中,而mobx是分模块建立store。
(2)设计方式不同:redux 基于发布订阅,而 mobx 基于Object.defineProperty 做数据劫持。所以mobx 中的action 可以直接修改state,而 redux 的 reducer 是 copy 一份 state,然后返回新的 state。
(3)编程思维方式不同:
- redux 更多的是基于函数式编程思想:reducer是个纯函数不直接改变状态、中间件的串行调用;
- mobx 更多基于面向对象和响应式编程思想。