此处引用知乎答主 @buhi 的一段话:
一句话,react context是给你注入服务的,不是让你注入数据的,如果要注入具有数据的服务那你就注入个类似EventEmitter的东西,例如rxjs observable 其实,我们可以把目光放到隔壁 UI 构建方式和 React 差不多的 Flutter 身上,它提供了一些新的思路,我们来看看 Flutter 下是怎么实现遥远组件间的状态共享的:
首先,Flutter 和 React 一样有类似 Context/Provider 的组件和接口:InheritedWidget,都能在组件树上很方便的共享数据,而无需通过 props 一层层往下传。
其次,Flutter 大部分官方组件都提供了一个叫做 Widget Controller 的东西,它的作用其实就有点类似上面 @buhi 提到 EventEmitter(在 Flutter 中的实现被叫做 ChangeNotifier),它使用了 观察者模式 来分发数据/事件给监听的组件,从而实现 在组件以外对组件内的状态进行控制。
在 React 下还原一下 Controller 的大概实现(部分为伪代码):
class Controller {
private _listeners = new Array<(title: string) => void>();
public addListener(listener: (title: string) => void) {
this._listeners.push(listener);
}
public removeListener(listener: (title: string) => void) {
this._listeners.remove(listener);
}
public updateTitle(title: string): void {
for (const listener of this._listeners) {
listener(title);
}
}
}
const Component: React.FC<{ controller: Controller }> = ({ controller }) => {
const [title, setTitle] = useState<string>();
const onChange = useCallback((newTitle: string) => {
setTitle(newTitle);
}, [setTitle]);
useEffect(() => {
controller.addListener(onChange);
return () => {
controller.removeListener(onChange);
};
}, [controller, onChange]);
return <>{title}</>;
};
可以看出:
- 核心是观察者模式,Controller 是发布者,Component 是观察者
- 可以在组件外部创建 Controller,并传递进组件内对组件进行 setState 控制
- 组件内通过 useEffect hook 来在确保仅在挂载周期内对 Controller 进行监听
当我们有了 Controller 这种能在组件以外对组件内的状态进行控制的「代理」,那我们就可以创建一个 Controller 实例并通过 Context 把它分享出去,从而实现遥远组件之间的相互控制:当我们想要改变受控组件的 state 时,不需要更新 Controller 的实例(因为会触发整个 Provider 树的更新),而只需调用 Controller 实例内的 updateTitle 方法即可。
当然,上面只是一个最小的例子,如果你对这种状态控制/分享方法感兴趣的话,可以看看鄙人封装的 hook(点击图片跳转):
目前已在多个 2C/2B 项目中使用,效果十分好。羽量级代码但却性能强悍,支撑起了大部分需要状态控制/分享的场景(PS:仓库已被知名写作 App 「Typora」的作者 Star 了~