组件不接收道具(React SSR)

时间:2017-05-18 14:16:50

标签: reactjs redux react-universal

我有一个Redux saga boilerplate的分支,我尝试通过操作更新服务器端的存储。它很顺利,但是当商店更新时,组件不会更新(不要调用mapStateToProps)。怎么了?求助。

Server log

组件来源:

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { testAction } from '../../actions';

class Event extends Component {
  static propTypes = {
    title: PropTypes.string,
    testAction: PropTypes.func
  }
  componentWillMount() {
    this.props.testAction({ test: 'test' });
  }
  render() {
    return (
      <div>
        Event - {this.props.title}
      </div>
    );
  }
}

function mapStateToProps(state) {
  console.log(state);
  return {
    title: state.default.test
  };
}

export default connect(
  mapStateToProps,
  { testAction }
)(Event);

server.js source:

import Express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import favicon from 'serve-favicon';
import compression from 'compression';
import http from 'http';
import proxy from 'express-http-proxy';
import path from 'path';
import url from 'url';
import { match, createMemoryHistory } from 'react-router';

import config from './config';
import configureStore from './store/configureStore';
import Html from './helpers/Html';
import getRoutes from './routes';
import waitAll from './sagas/waitAll';
import { Root } from 'containers';

const app = new Express();
const server = new http.Server(app);

// disable `X-Powered-By` HTTP header
app.disable('x-powered-by');

app.use(compression());
app.use(favicon(path.join(__dirname, '..', 'static', 'favicon.ico')));
app.use(Express.static(path.join(__dirname, '..', 'static')));

// Proxy to API
app.use('/api', proxy(config.apiBaseUrl, {
  // eslint-disable-next-line
  forwardPath: (req, res) => url.parse(req.url).path
}));

app.use((req, res) => {
  if (__DEVELOPMENT__) {
    webpackIsomorphicTools.refresh();
  }

  const memoryHistory = createMemoryHistory();
  const store = configureStore();
  const allRoutes = getRoutes(store);
  const assets = webpackIsomorphicTools.assets();

  function hydrateOnClient() {
    const htmlComponent = <Html assets={assets} store={store} />;
    const renderedDomString = ReactDOMServer.renderToString(htmlComponent);
    res.send(`<!doctype html>\n ${renderedDomString}`);
  }

  if (__DISABLE_SSR__) {
    hydrateOnClient();
    return;
  }

  match({ routes: allRoutes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (redirectLocation) {
      res.redirect(redirectLocation.pathname + redirectLocation.search);
    } else if (error) {
      console.error('ROUTER ERROR:', error);
      res.status(500);
      hydrateOnClient();
    } else if (renderProps) {
      const preloaders = renderProps.components
      .filter((component) => component && component.preload)
      .map((component) => component.preload(renderProps.params, req))
      .reduce((result, preloader) => result.concat(preloader), []);

      const runTasks = store.runSaga(waitAll(preloaders));

      runTasks.done.then(() => {
        const rootComponent = (<Root
          store={store}
          routes={allRoutes}
          history={memoryHistory}
          renderProps={renderProps}
          type="server"
        />);
        const htmlComponent = <Html assets={assets} component={rootComponent} store={store} />;
        const renderedDomString = ReactDOMServer.renderToString(htmlComponent);

        global.navigator = { userAgent: req.headers['user-agent'] };
        res.status(200).send(`<!doctype html>\n ${renderedDomString}`);
      }).catch((e) => {
        console.log(e.stack);
      });

      store.close();
    } else {
      res.status(404).send('Not found');
    }
  });
});

if (config.port) {
  server.listen(config.port, (err) => {
    if (err) {
      console.error(err);
    }
    console.info('==>   Open http://%s:%s in a browser to view the app.', config.host, config.port);
  });
} else {
  console.error('==>     ERROR: No PORT environment variable has been specified');
}

1 个答案:

答案 0 :(得分:0)

一般来说,如果你使用服务器端渲染,那么提前收集初始状态并以一条线的形式在客户端传输它会更简单,更有效。

标准操作图大致如下:你有几个promises函数从某些来源获取数据。当需要客户端上已经运行的页面的地址时,您只需在获取时启动传奇,并快速广播来自源的响应。

在SSR的情况下,您事先就会对所有操作进行汇总承诺,并以序列化对象的形式发送它。例如:

Promise.all([ getData1(), getData2() ]).then((initialState) => {
    const store = createStore(handlers, initialState);
    // .....
    const htmlComponent = <Html assets={assets} component={rootComponent} store={store} />;
    const renderedDomString = ReactDOMServer.renderToString(htmlComponent);
})

注意:在React的未来版本中,随着光纤承诺异步呈现的引入,ReactDOMServer.renderToString函数已经实现,Promise将允许这样,并以异步模式执行对数据源的任意上诉序列。 / p>