【问题标题】:React with Redux and Router - server renderingReact 与 Redux 和 Router - 服务器渲染
【发布时间】:2017-01-05 12:14:15
【问题描述】:

我正在尝试通过异步调用使服务器渲染工作。我的问题是,在渲染之前没有下载数据。

我的代码(也可以在 github 上找到:https://github.com/tomekbuszewski/server-rendering

server.js

require('babel-register');

import express from 'express';
import path from 'path';

import React from 'react';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import { getState } from 'redux';
import { match, RouterContext } from 'react-router'

import routes from './app/routes';
import store from './app/store';

const app = express();
const port         = 666;

app.get('/', (req, res) => {
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (renderProps) {
      console.log(renderProps.components);
      res.send(renderToString(
        <Provider store={store}>
          <RouterContext {...renderProps} />
        </Provider>
      ))
    } else {
      console.log('err')
    }
  });
});

app.use('/public', express.static(path.join(__dirname, 'public')));
app.use('/data', express.static(path.join(__dirname, 'data')));

app.listen(port);
console.log(`localhost:${port}`);

routes.js

import React from 'react';
import { Router, Route, browserHistory } from 'react-router';

import App from './Components/App';

const routes = (
  <Router history={browserHistory}>
    <Route path="/" component={App} />
  </Router>
);

export default routes;

App 组件有 fetch 功能:

const fetch = (dispatch) => {
    const endpoint = '/data/index.json';

    axios.get(endpoint).then((res) => {
        Array.prototype.forEach.call(res.data, d => {
            const payload = {
                id:    d.id,
                title: d.title
            };

            dispatch({type: 'ADD_CONTENT', payload});
        });
    });
};

调度“ADD_CONTENT”操作:

case 'ADD_CONTENT':
  return { ...state, data: [...state.data, action.payload] };

在客户端一切正常。

【问题讨论】:

    标签: javascript reactjs react-router react-redux


    【解决方案1】:
    1. 你的 redux 操作应该返回一个 promise,当 store 正确加载数据时该 promise

    例如:

    const fetch = (dispatch) => {
        const endpoint = '/data/index.json';
    
        return axios.get(endpoint).then((res) => {
            Array.prototype.forEach.call(res.data, d => {
                const payload = {
                    id:    d.id,
                    title: d.title
                };
    
                dispatch({type: 'ADD_CONTENT', payload});
            });
        });
    };
    
    1. 您必须使用像redux-connect 这样的包装器,在加载组件之前调用这些操作。

    在您的示例中,路由组件是App

    在这种情况下,您可以使用预加载数据所需的 redux 操作来装饰该组件。

    import { asyncConnect } from 'redux-connect';
    
    @asyncConnect([{
        promise: ({ store: { dispatch } }) => {
            // `fetch` is your redux action returning a promise
            return dispatch(fetch());
        }
    }])
    @connect(
        state => ({
            // At this point, you can access your redux data as the
            // fetch will have finished 
            data: state.myReducer.data
        })
    )
    export default class App extends Component {
        // ...the `data` prop here is preloaded even on the server
    }
    

    @connect 可以装饰嵌套组件,如果您不使用顶部容器中的数据,因为商店在应用程序范围内可用,但 @asyncConnect 必须装饰由 react-router 直接调用的组件,它不能(到目前为止)在渲染树中的嵌套组件上使用,或者在初始渲染期间不会解析它们

    由于组件树的渲染只在服务端发生一次,该函数不能被组件本身调用,需要在加载之前调用通过 redux-connect 和支持 promise 的 redux 操作,例如使用 redux-thunk 中间件。

    【讨论】:

    • 感谢您提供有关redux-connect 的信息,但我不得不承认他们的示例非常令人困惑。我得到了服务器路径,但组件一对我来说很奇怪。我应该如何将我的数据与@asyncConnect 连接起来,我应该放弃我的减速器并只使用reduxAsyncConnect
    • 在您的主要combineReducers(...) 调用中,您只需包含来自redux-connect 的reducer,而不会对您的其他reducer 产生任何其他影响。我不知道它是否是强制性的,也许是为了跟踪预加载状态,我个人在我的布局中使用它来在客户端加载时显示“加载”消息,ie 所有后续路线在第一个 SSR 之后发生变化。
    • 但我同意它不容易使用,需要尝试一下 :-) 我没有发现 更容易。 (几个月前)
    • 好的,所以第一个问题又回来了——我应该如何在我当前的代码中使用asyncConnect
    • 我用reduxAsyncConnect的实际组件声明更新了我的答案
    猜你喜欢
    • 2016-03-26
    • 1970-01-01
    • 2016-02-22
    • 1970-01-01
    • 2016-11-17
    • 2023-04-10
    • 2021-01-05
    • 2015-08-18
    • 2017-11-12
    相关资源
    最近更新 更多