Redux服务器端渲染:操作

时间:2017-01-07 08:00:38

标签: reactjs redux isomorphic-javascript serverside-rendering

我正在使用ReactRedux构建一个通用应用,它通过NodeJSExpressJS在服务器端呈现。

一切正常,Express处理程序调用{​​{1}},每次都会创建{match} from 'react-router'

我遇到的问题是:new store instance只呈现商店的{renderToString} from 'react-dom/server'版本,如果商店更改某些商品(例如:通过{{调度的行动) 1}})商店将会更新,但pristine在调用新的componentWillMount之前不会更改。

  1. 我不知道(每个请求)reducers会如何改变状态,因此,在调用generated markup之前,我无法提供初始状态。
  2. 我想避免再次renderToString来电。
  3. 这是我的示例代码:

    renderToString

2 个答案:

答案 0 :(得分:1)

一种方法是在Route组件上提供每路由操作作为静态方法,然后在调用renderToString之前由您的快速中间件调用,例如。

class Index extends Component {
   componentDidMount() {
       // NOTE: The client fetches the data in `componentDidMount` instead of `componentWillMount` to ensure we don't duplicate efforts on the server.
       const { dispatch } = this.props;
       Index.fetchData({ dispatch });
   }

   render() {
       ...
   }
}

Index.fetchData = function({ dispatch }) {
    // Returns promise via redux-thunk / redux-promise
    return dispatch(fetchDataIfNeeded());
};

export default connect(state => state)(Index);

然后在您的中间件中,您将在呈现之前调用匹配的路由组件上定义的所有静态fetchData方法,例如

app.use((req, res) => {
    match({
        routes,
        location: req.url
    }, (error, redirectLocation, renderProps) => {
        const store = configureStore();
        const requests = renderProps.components
            .map(component => {
                // Handle `connect`ed components.
                if (component.WrappedComponent) {
                    component = component.WrappedComponent;
                }
                if (component.fetchData) {
                    return component.fetchData({
                        dispatch: store.dispatch
                    })
                    .catch(() => {});
                }
            });
        Promise.all(requests)
            .then(() => {
                // Store now contains all necessary state.
                const markup = renderToString(
                    <Provider store={store}>
                        <RouterContext {...renderProps} />
                    </Provider>
                );
                res.send(`<!doctype html><html><body>${markup}</body></html>`);
            });
    });
});

您可以在60frames boilerplate中找到完整示例。

相关模块:

https://github.com/60frames/react-boilerplate/blob/master/src/components/index/Index.js

https://github.com/60frames/react-boilerplate/blob/master/src/server.js

答案 1 :(得分:1)

另一种找到的方法:

使用Redux-Saga

https://medium.com/@navgarcha7891/react-server-side-rendering-with-simple-redux-store-hydration-9f77ab66900a#.xin76jnk4

赞成

  1. 避免使用特定server-side附加代码
  2. 以Redux方式入住
  3. 缺点

    1. 两次致电renderToString
    2. 示例:

      import { END } from 'redux-saga';
      
      store.runSaga = sagaMiddleware.run;
      store.close = () => store.dispatch(END);
      
      
      // server.js
      store.runSaga(sagas).done.then(() => {
          const state = store.getState();
          const html = renderToString(rootComponent);
      
          reply(renderApplication({state, html}));
      });
      
      renderToString(rootComponent);
      
      store.close();