如何调用更新反应上下文状态/或从非反应类调用方法?

时间:2019-11-02 21:53:22

标签: reactjs react-redux react-context

我有一个反应上下文,该上下文被用作跨多个组件的提供程序。

我想修改该状态并完全从React组件之外的常规方法更新UI。即不是功能组件或类,而只是普通的JS代码。

据我所知,只能从render方法或React.FC中访问提供者/上下文

I can pass a function in,但似乎只是夹在<Context.Provider>之间的某种类型的React组件

useContext仅在相关项目传递到渲染循环后才会亮起。

有没有一种方法可以创建某种类型的store,我可以对其数据调用一个setState()方法,但是这种更新在UI中是反应性的?

[edit]我想要这的原因是cos,我有一个外部API调用,该调用使我可以长时间运行。我不需要将API封装在各种反应性的东西中,因为我希望该模块具有可移植性(例如,到服务器端),并且只需与应用程序状态进行交互即可更新UI显示。

2 个答案:

答案 0 :(得分:1)

据我所知,如果要使用React构建UI,仍需要选择使用React API。

这是您需要做的:

  • 需要一个上下文来在整个组件树中共享价值
  • 在根上需要提供程序组件
  • 需要订阅外部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>