Redux - 异步加载初始状态

时间:2016-05-23 14:03:54

标签: javascript redux flux

我正试图找出最简洁的方法来加载我的Redux商店的初始状态来源于API调用。

据我所知,提供初始状态的典型方法是在页面加载时生成服务器端,并将其作为简单对象提供给Redux createStore()。但是,我正在编写一个我计划在Electron中打包的应用程序,所以这不起作用。

到目前为止,我能够提出的最好的方法是在创建将要去商店的初始状态的商店后立即触发一个动作 - 要么是一个检索整个初始状态的动作,要么是每个检索商店一部分的初始状态的操作数。这意味着我的代码看起来像:

const store = createStore(reducer, Immutable.Map(), middleware);
store.dispatch(loadStateForA());
store.dispatch(loadStateForB());
store.dispatch(loadStateForC());

虽然这会起作用,但看起来有点像粗糙的一面,所以我想知道是否有一些更好的替代方案我缺席了?

4 个答案:

答案 0 :(得分:3)

我也遇到了同样的问题(也在构建一个电子应用程序)。我的商店的一部分具有应用程序设置,这些设置会持久存储在本地文件系统上,我需要在应用程序的启动时异步加载它。

这就是我想出的。作为React / Redux的“新手”,我非常有兴趣了解社区对我的方法的看法以及如何改进。

我创建了一个异步加载存储的方法。此方法返回Promise,其中包含store对象。

export const configureStoreAsync = () => {
  return new Promise((resolve) => {
    const initialState = initialStoreState;//default initial store state
    try {
        //do some async stuff here to manipulate initial state...like read from local disk etc. 
        //This is again wrapped in its own Promises.
        const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
        resolve(store);
      });
    } catch (error) {
      //To do .... log error!
      const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
      console.log(store.getState());
      resolve(store);
    }
  });
};

然后在我的应用程序入口点,这是我如何使用它:

configureStoreAsync().then(result => {
  const store = result;
  return ReactDOM.render(
    <Provider store={store}>
      <App store={store}/>
    </Provider>,
    document.getElementById('Main'));
});

就像我说的,这是我解决这个问题的天真尝试,我相信必须有更好的方法来解决这个问题。我非常想知道如何改进它。

答案 1 :(得分:2)

据我所知,你只有两个选择(逻辑上):

  1. 在实例化商店后
  2. 设置初始状态
  3. 设置初始状态 实例化
  4. 选项1 必须使用操作完成:

      

    改变状态的唯一方法是发出一个动作,一个对象   描述发生的事情。

    - One of "Three Principles" in the docs

    这是你尝试过的,但你认为它原因很粗糙。

    另一种方法是在异步请求解决后调用createStore。已经使用Promise对象发布了一个解决方案(由@Gaurav Mantri提供),这是一种很好的方法。

    我建议您不要这样做,因为在您的商店(或requireimport)存在之前,您可能会有多个模块尝试store.dispatchstore.subscribe;他们都必须被期待Promise。第一种方法是Redux-y。

答案 2 :(得分:2)

我的应用启动工作流程:

  1. 在index.html中加载微调器
  2. 用于检查用户是否已登录的Ajax
  3. 在ajax端,渲染根组件
  4. 隐藏加载微调器
  5. 我通过以下方式实现了这一目标:

    1. 使用自定义中间件创建商店,该中间件侦听初始ajax结束操作并调用回调一次
    2. 调度初始ajax操作
    3. root.js

      const store = createStore(
          rootReducer,
          applyMiddleware(
              ...,
              actionCallbackOnceMiddleware(INITIAL_AJAX_END, render)
          )
      )
      
      function render() {
          ReactDOM.render(
              <Provider store={store}>
                  <RootComponent/>
              </Provider>,
              document.getElementById('root')
          )
      
          document.getElementById('loading').dispatchEvent(new Event('hide'))
      }
      
      store.dispatch(initialAjaxAction());
      

      middleware/actionCallbackOnce.js

      export default (actionType, callback) => store => next => {
          let called = false;
      
          return action => {
              next(action);
      
              if (!called && action.type === actionType) {
                  called = true;
                  callback();
              }
          }
      }
      

      index.html

      <div id="loading">
          <span>Loading</span>
          <style type="text/css">...</style>
          <script>
              (function(loading){
                  loading.addEventListener('hide', function(){
                      loading.remove();
                  });
                  loading.addEventListener('error', function(){
                      loading.querySelector('span').textContent = "Error";
                  });
              })(document.getElementById('loading'));
          </script>
      </div>
      
      <div id="root"></div>
      

答案 3 :(得分:0)

使用 extraReducerscreateAsyncThunk 似乎是这样做的干净方式here