1

と を介してサーバー側でレンダリングするReactとを使用してユニバーサル アプリを構築しています。ReduxNodeJSExpressJS

すべて正常に動作しており、Express ハンドラが呼び出され{match} from 'react-router'new store instance毎回作成されます。

私が抱えている問題{renderToString} from 'react-dom/server'は、ストアのバージョンのみをレンダリングすることです。pristineストアが変更された場合 (例: を介してディスパッチされたアクションcomponentWillMount)、ストアは更新されますがgenerated markup、 newrenderToStringが呼び出されるまでは変更されません。

  1. レデューサーがどのように (リクエストごとに) 状態を変更するかわからないため、 を呼び出す前に初期状態を提供することはできませんrenderToString
  2. これ以上の電話は避けたいと思いrenderToStringます。

これは私のサンプルコードです:

const store = createStore(
  reducers,

  // an object that provides the initial generic state
  res.viewModel.__INITIAL_STATE__ || Object.create(null),

  middlewares
);

// now the store is pristine and calling store.getState()
// I will retrieve an invalid version of the state

const markup = renderToString(
  <Provider store={store}>
    {<RouterContext {...renderProps} />}
  </Provider>
);

// now the store is correctly computed and calling
// store.getState() gets the right version but the markup
// generated is still old. Only recalling `renderToString` will get 
// the right markup

const markup2 = renderToString(
  <Provider store={store}>
    {<RouterContext {...renderProps} />}
  </Provider>
);
4

2 に答える 2

1

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 ボイラープレートで完全な例を見ることができます。

関連モジュール:

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

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

于 2017-01-08T13:09:19.880 に答える