如何将react-router和redux逻辑组合起来进行客户端和服务器端呈现

时间:2015-08-28 14:37:54

标签: reactjs react-router isomorphic-javascript redux

我希望我的基于React的SPA能够在服务器端呈现(这些日子不是这样)。因此,我想将React与react-routerredux和一些构建层(如isomorphic starterkit)结合起来。

hapi universal redux加在一起,但我正在努力如何组织我的流程。我的数据来自REST API的多个端点。不同的组件具有不同的数据需求,应该及时在客户端上加载数据。在服务器上,必须获取特定路由(组件集)的所有数据,并将必要的组件呈现给字符串。

在我的第一种方法中,我使用了redux的中间件来创建异步操作,这些操作加载数据,返回一个promise,并在promise解析时触发SOME_DATA_ARRIVED操作。 Reducers然后更新我的商店,组件重新渲染,一切都很好。原则上,这是有效的。但后来我意识到,在路由发挥作用的那一刻,流程变得尴尬。

列出许多数据记录的某个组件有多个链接来过滤记录。每个过滤后的数据集都应该通过自己的网址/filter-by/:filter提供。所以我使用不同的<Link to={...}>组件来更改点击的URL并触发路由器。路由器应根据当前URL所代表的状态更新存储,从而导致重新呈现相关组件。

这不容易实现。我首先尝试componentWillUpdate来触发一个动作,该动作异步加载我的数据,填充存储并为我的组件引起另一个重新渲染周期。但这不适用于服务器,因为只支持3种生命周期方法。

所以我正在寻找正确的方法来组织这个。用户与应用程序的交互(从用户角度更改应用程序状态)应更新URL。 IMO这应该使路由器以某种方式加载必要的数据,更新商店,并开始对帐过程。

所以interaction -> URL change -> data fetching -> store update -> re-render

此方法也应该在服务器上运行,因为从请求的URL中,应该能够确定要加载的数据,生成initial state并将该状态传递到redux的store代。但我找不到妥善做到这一点的方法。所以对我来说,出现了以下问题:

  1. 我的方法是错误的,因为有些东西我不明白/不知道吗?
  2. 在redux的store中保持从REST API加载的数据是正确的吗?
  3. 组件在redux state中保留store而其他人在自己管理state时是不是有点尴尬?
  4. 想法让interaction -> URL change -> data fetching -> store update -> re-render完全错了吗?
  5. 我愿意接受各种建议。

1 个答案:

答案 0 :(得分:4)

我今天确实设置了完全相同的东西。我们已经拥有的是react-router和redux。我们模块化了一些模块,将东西注入其中 - 而中提琴 - 它起作用。我使用https://github.com/erikras/react-redux-universal-hot-example作为参考。

部分:

1。 router.js

我们返回function (location, history, store)以使用promises设置路由器。 routes是包含所有组件的react-router的路由定义。

module.exports = function (location, history, store) {
    return new Bluebird((resolve, reject)  => {
        Router.run(routes, location, (Handler, state) => {
            const HandlerConnected = connect(_.identity)(Handler);
            const component = (
                <Provider store={store}>
                    {() => <HandlerConnected />}
                </Provider>
            );
            resolve(component);
        }).catch(console.error.bind(console));
    });
};

2。 store.js

您只需将初始状态传递给createStore(reducer, initialState)即可。您只需在服务器和客户端上执行此操作。对于客户端,您应该通过脚本标记(即window.__initialstate__)使状态可用。 有关详细信息,请参阅http://rackt.github.io/redux/docs/recipes/ServerRendering.html

3。在服务器上呈现

获取您的数据,使用该数据设置初始状态(...data)。上面的createRouter = router.jsres.render表示使用以下

呈现玉石模板
script.
    window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'};
...
#react-start
    != html
var initialState = { ...data };
var store = createStore(reducer, initialState);
createRouter(req.url, null, store).then(function (component) {
    var html = React.renderToString(component);
    res.render('community/neighbourhood', { html: html, initialState: initialState });
});

4。适应客户

然后你的客户可以做同样的事情。来自React-Router

location可能是HistoryLocation
const initialState = window.csvistate.__initialstate__;
const store = require('./store')(initialState);

router(location, null, store).then(component => {
    React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]);
});

回答你的问题:

  1. 你的方法似乎是对的。我们也这样做。人们甚至可以将网址作为州的一部分。
  2. redux商店内的所有州都是好事。这样你就有了一个单一的事实来源。
  3. 我们现在还在研究应该去哪里。目前,我们要求服务器上componentDidMount上的数据已经存在。