我希望我的基于React的SPA能够在服务器端呈现(这些日子不是这样)。因此,我想将React与react-router,redux和一些构建层(如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
代。但我找不到妥善做到这一点的方法。所以对我来说,出现了以下问题:
store
中保持从REST API加载的数据是正确的吗?state
中保留store
而其他人在自己管理state
时是不是有点尴尬?interaction -> URL change -> data fetching -> store update -> re-render
完全错了吗?我愿意接受各种建议。
答案 0 :(得分:4)
我今天确实设置了完全相同的东西。我们已经拥有的是react-router和redux。我们模块化了一些模块,将东西注入其中 - 而中提琴 - 它起作用。我使用https://github.com/erikras/react-redux-universal-hot-example作为参考。
部分:
我们返回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));
});
};
您只需将初始状态传递给createStore(reducer, initialState)
即可。您只需在服务器和客户端上执行此操作。对于客户端,您应该通过脚本标记(即window.__initialstate__
)使状态可用。
有关详细信息,请参阅http://rackt.github.io/redux/docs/recipes/ServerRendering.html。
获取您的数据,使用该数据设置初始状态(...data
)。上面的createRouter
= router.js
。 res.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 });
});
然后你的客户可以做同样的事情。来自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]);
});
componentDidMount
上的数据已经存在。