我有一个反应上下文,该上下文被用作跨多个组件的提供程序。
我想修改该状态并完全从React组件之外的常规方法更新UI。即不是功能组件或类,而只是普通的JS代码。
据我所知,只能从render方法或React.FC中访问提供者/上下文
I can pass a function in,但似乎只是夹在<Context.Provider>
之间的某种类型的React组件
和useContext
仅在相关项目传递到渲染循环后才会亮起。
有没有一种方法可以创建某种类型的store
,我可以对其数据调用一个setState()
方法,但是这种更新在UI中是反应性的?
[edit]我想要这的原因是cos,我有一个外部API调用,该调用使我可以长时间运行。我不需要将API封装在各种反应性的东西中,因为我希望该模块具有可移植性(例如,到服务器端),并且只需与应用程序状态进行交互即可更新UI显示。
答案 0 :(得分:1)
据我所知,如果要使用React构建UI,仍需要选择使用React API。
这是您需要做的:
这是an example的工作方式
答案 1 :(得分:0)
我假设您是在问这个问题,因为您没有使用像redux这样的状态管理器,您可以在其中从任何地方分派操作来重置状态。
一个选项;正如您已经提到的;是创建自己的商店和商店提供商。也许这样的事情对您有用:
const store = (initialState => {
let value = initialState;
let listeners = [];
const getState = () => value;
const setState = fn => {
value = fn(value);
listeners.forEach(l => l(value));
};
const subscribe = listener => {
listeners.push(listener);
return () =>
(listeners = listeners.filter(f => f !== listener));
};
return { getState, setState, subscribe };
})({ counter: 1 }); //pass initial state to IIFE
const Store = React.createContext();
function Provider({ store, children }) {
const [state, setState] = React.useState(
store.getState()
);
React.useEffect(
() =>
store.subscribe(() => {
const lastState = store.getState();
//if a lot of setState calls are made synchronously
// do not update dom but let it batch update state
// before triggering a render
Promise.resolve().then(() => {
if (lastState === store.getState()) {
setState(store.getState());
}
});
}),
[store]
);
return (
<Store.Provider value={state}>
{children}
</Store.Provider>
);
}
function App() {
const state = React.useContext(Store);
return <div>{state.counter}</div>;
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
//call store setState from anywhere
setInterval(
() =>
store.setState(state => ({
...state,
counter: state.counter + 1,
})),
1000
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>