我正在尝试找到一种好的,干净的,几乎没有样板的方法来处理React的全局状态
这里的想法是要有一个HOC,利用React的新Hooks&Context API来返回一个Context提供者,该提供者具有绑定到其状态的值。我使用rxjs触发商店更改的状态更新。
我还从商店中导出了更多对象(值得注意的是:原始rxjs主题对象和商店的Proxy(总是返回最新值)。
这有效。当我在全局存储区中进行更改时,我会在应用程序中的任何位置(无论是React组件,还是React外部)获得更新。但是,要实现此目的,HOC组件会重新渲染。
这是没有人吗?
我认为可能有问题的代码/逻辑是HOC组件:
const Provider = ({ children }) => {
const [store, setStore] = useState(GlobalStore.value)
useEffect(() => {
GlobalStore.subscribe(setStore)
}, [])
return <Context.Provider value={store}>{children}</Context.Provider>
}
GlobalStore
是rxjs BehaviorSubject
。每次更新主题时,Provider
组件的状态都会更新,从而触发重新渲染。
此处提供了完整的演示:https://codesandbox.io/s/qzkqrm698q
真正的问题是:这不是进行全球状态管理的糟糕方法吗?我觉得可能是因为我基本上在状态更新时重新渲染了所有内容...
编辑:我认为我编写了一个性能更高的版本,该版本不是轻量级的(取决于MobX),但我认为它产生的开销要少得多(演示在https://codesandbox.io/s/7oxko37rq上) -现在将有相同的最终结果,但是删除MobX是很酷的-这个问题不再有意义
答案 0 :(得分:1)
我了解您需要处理全局状态。我已经发现自己处于同样的境地。我们采用了类似的解决方案,但就我而言,我决定完全放弃 ContextAPI。
ContextAPI 对我来说真的很糟糕。它似乎假装是基于控制器的模式,但您最终将代码包装在无意义的 HOC 中。也许我没有注意到他在这里的观点,但在我看来,ContextAPI 只是一种提供基于范围的数据流的复杂方式。
因此,我决定使用 React Hooks 和 RxJS 实现自己的全局状态管理器。主要是因为我不习惯在真正的大型项目上工作(Redux 非常适合)。
我的解决方案很简单。所以让我们阅读一些代码,因为它们说的不仅仅是单词:
我创建了一个仅用于 dar nome aos bois 的类(这是一个流行的巴西表达,谷歌它?)并且有一个简单的方法来使用 BehaviorSubject 值的部分更新:
import { BehaviorSubject } from "rxjs";
export default class Store<T extends Object> extends BehaviorSubject<T> {
update(value: Partial<T>) {
this.next({ ...this.value, ...value });
}
}
实例化 Store 类的函数(是的,这只是因为我不喜欢键入 new
¯\(ツ)/¯):
import Store from "./store";
export default function <T>(initialValue: T) {
return new Store<T>(initialValue);
}
我创建了一个钩子来轻松使用与 Store
连接的本地状态:
import Store from "./store";
import { useCallback, useEffect, useState } from "react";
import { skip } from "rxjs/operators";
import createSharedStore from "./createSharedStore";
const globalStore = createSharedStore<any>({});
type SetPartialSharedStateAction<S> = (state: S) => S;
type SetSharedStateAction<S> = (
state: S | SetPartialSharedStateAction<S>
) => void;
export default function <T>(
store: Store<T> = globalStore
): [T, SetSharedStateAction<T>] {
const [state, setState] = useState(store.value);
useEffect(() => {
const subscription = store
.pipe(skip(1))
.subscribe((data) => setState(data));
return () => subscription.unsubscribe();
});
const setStateProxy = useCallback(
(state: T | SetPartialSharedStateAction<T>) => {
if (typeof state === "function") {
const partialUpdate: any = state;
store.next(partialUpdate(store.value));
} else {
store.next(state);
}
},
[store]
);
return [state, setStateProxy];
}
然后我为需要共享状态的每个功能导出单独的商店:
import { createSharedStore } from "hooks/SharedState";
export default createSharedStore<Models.Example | undefined>(undefined);
最后,这是如何在组件中使用(就像常规的 React 状态一样):
import React from "react";
import { useSharedState } from "hooks/SharedState";
import ExampleStore from "stores/ExampleStore";
export default function () {
// ...
const [state, setState] = useSharedState(ExampleStore);
// ...
function handleChanges(event) {
setState(event.currentTarget.value);
}
return (
<>
<h1>{state.foo}</h1>
<input onChange={handleChange} />
</>
);
}
答案 1 :(得分:0)
GlobalStore
主题是多余的。 RxJS observables和React上下文API都实现了pub-sub模式,以这种方式一起使用它们没有任何好处。如果应该在孩子中使用GlobalStore.subscribe
来更新状态,则将导致不必要的紧密耦合。
使用新对象更新glubal状态将导致重新渲染整个组件层次结构。避免儿童出现性能问题的一种常见方法是选择必要的状态部分,并使它们成为纯组件,以防止不必要的更新:
<Context.Consumer>
({ foo: { bar }, setState }) => <PureFoo bar={bar} setState={setState}/>
</Context.Provider>
只要PureFoo
和bar
相同, setState
就不会在状态更新时重新呈现。