如何在react-router转换期间更新redux存储?

时间:2015-12-23 21:40:07

标签: reactjs react-router redux

我遇到了在发生反应路由器转换时如何更新存储的问题。

在我当前的实现(下面)中,在渲染下一页之前更新存储。当前页面根据下一页的数据获得商店更新时会出现问题:(1)当前页面呈现无意义(它订阅了商店更新),因为更新的商店用于下一页(2)当前页面在渲染时中断,因为更新的存储只有下一页的数据。

superagent
  .get(opts.path)
  .set('Accept', 'application/json')
  .end((err, res) => {
    let pageData = res && res.body || {};
    store.dispatch(setPageStore(pageData));
    render(store);
  });

反过来也有问题,在更新商店之前渲染下一页。现在的问题是渲染的下一个分页符,因为在更新存储之前,下一页所需的数据不存在。

我要么滥用这些库,要么我的架构不完整,或其他什么。帮助!

示例代码的其余部分:

应用

const React = require('react');
const Router = require('react-router');
const {createStore} = require('redux');
const {update} = React.addons;
const routes = require('./routes'); // all the routes
let store = {};
let initialLoad = true;

Router.run(routes, Router.HistoryLocation, (Handler, opts) => {
  if(initialLoad) {
    initialLoad = false;

    // hydrate
    const initialState = JSON.parse(document.getElementById('initial-state').text);
    store = createStore(appReducer, initialState);
    render(store);

  } else {
    superagent
      .get(opts.path)
      .set('Accept', 'application/json')
      .end((err, res) => {
        let pageData = res && res.body || {};
        store.dispatch(setPageStore(pageData));
        render(store);
      });
  }
});

function render(store) {
  React.render(
    <Provider store={store} children={() => <Handler/>} />, 
    document.getElementById('react')
  );
}

动作&amp;减速器

function appReducer(state = {}, action) {
  switch(action.type) {
    case 'SET_PAGE_STORE':
      return update(state, {$merge: action.pageData});

    default:
      return reduce(state, action);
  }
}

const reduce = combineReducers({
  // ..all the reducers
});

function setPageStore(pageData) {
  return {type: 'SET_PAGE_STORE', pageData};
}

2 个答案:

答案 0 :(得分:5)

您可以使用redux-thunk中间件一个接一个地分派多个动作

查看更棒的redux doc #Async Actions section以获取更多信息!

所以你的获取数据动作创建者看起来像这样:

function fetchSomeData(path) {
  return dispatch => {
    // first dispatch a action to start the spinner
    dispatch(fetchStarted(path))

    return superagent.get(path)
      .set('Accept', 'application/json')
      .end((err, res) => {
        if (err) {
          dispatch(fetchDataError(err)) // handle error
        } else {
          let pageData = res && res.body || {};
          dispatch(fetchSuccess(pageData)); // do whatever you want when fetch is done here
          // such as this action from redux-simple-router to change route
          dispatch(pushPath('/some/path'))
      });
  }
}

正如您所看到的,只需执行store.dispatch(fetchSomeData('somePath')),它就会自动首先调用fetchStarted来显示微调器,当进程完成后,调用fetchSuccess(path)隐藏微调器,更新状态,重新渲染...等,或致电fetchError(err)以显示错误消息,您可以调用操作来在此过程中的任何位置更改路线!

(如果你不喜欢它,你不需要简易路由器,你可以调用history.pushState(null, '/some/path')来改变路由,我只是发现redux-simple-router非常方便,因为你不需要在任何地方传递历史记录,如果您想跟踪路线更改,您还可以收听UPDATE_PATH行动。

此外,当您将react-router与redux一起使用时,我建议使用redux-simple-router,它允许您使用UPDATE_PATH操作类型和pushPath操作来更改路由。 另外,我注意到您使用的是反应路由器的过时版本......

如果你想使用最新版本的react-router和redux-simple-router(连同redux-thunk),check out this repo

你可以在这些文件中找到它的商店配置,路由器设置:

src/main.js                 // calls reactDOM.render(<Root />, ...) to render <Root />
src/containers/Root.js      // <Root /> is a wrapper for react-redux <Provider />
src/redux/configureStore.js // store configuration, how redux-thunk middleware is configured
src/routes/index.js         // routes are defined here

答案 1 :(得分:3)

一种解决方案是让您的页面与不完整的数据兼容,直到获取新的页面数据。然后你可以推送新路径,它会尽可能多地渲染,你的UI组件会渲染微调器(或类似的东西),直到获取下一页的数据并将其分派到商店。在第二遍中,页面将被完全重新渲染。

另一件需要注意的事情是,理想情况下,商店的形状使您的所有网页都能与所有可能存在的状态完全兼容,而不会产生冲突。这将允许您执行诸如预取数据之类的操作,这可以解锁最小化转换时间的方法,以及缓存先前的视图。毕竟,单页架构在仍然需要往返时并不是特别有用。