使用从 API 调用的数据响应服务器端渲染

React server side rendering using data called from API

我使用 react-redux + thunk + redial 并在服务器端生成我的 HTML。我想通过 react-helmet 使用来自另一台服务器的用户数据生成元标记。 在我的服务器中:

import { trigger } from 'redial';
trigger('fetch', components, locals)
      .then(() => {
          const state = getState();
          var html = ReactDOMServer.renderToString (
              <Provider store={store} key="provider">
                  <ReactRouter.RouterContext {...renderProps} />
              </Provider>
          );
let head = Helmet.rewind();
response.send(`<!DOCTYPE html>
              <head>
                <meta charSet="UTF-8"></meta>
                <meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1"></meta>
                <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
                <link rel='stylesheet' href='/css/bootstrap.css'/>
                ${style? "<style>"+style+"</style>" : "<link rel='stylesheet' href='/css/app.css'/>"}
                    ${head.title}
                    ${head.meta}
                    ${head.link}
                    ${head.script}
                </head>
                <body>
                  <div id="app">${html}</div>
                  <script src='/javascript/bundle.js'></script>
                     ${popUp}
                </body>
              </html>`);

在我的组件中,我在 render() 函数中调用 dispatch 到 运行 动作文件中的一个函数,该函数调用 API 和 returns 用户数据。无论我在哪里使用 dispatch,它都有相同的结果。在最好的情况下 React returns 请求并等待来自其他服务器的数据。但它 returns 由于我设置检查从服务器接收到的数据的条件,没有任何问题。

我的组件

    var profile=[], shouldRender = false
export var Profile = React.createClass({
render () {
  var {x,y,z} = this.props;
  var pathArray = (this.props.path).split( '/' );
  if(!canUseDOM && shouldRender == false)dispatch(
    actions.theProfile(x,y,z)
  ).then((es) => {profile = es; shouldRender= true;Promise.resolve()})
  .catch((et) => console.log("error"));

if(shouldRender){
  console.log("got inside");
  return (
    <div  className="container">
      <RightSideBar page="profile"/>
      <Helmet
            title={metaData('title', name, profileId)}/>
      <div className="col-md-6 col-xs-12 col-md-offset-3 content right">
        <div className="row">
          {profileContent(ideaTypes)}
        </div>
      </div>
      <div className="col-md-3 col-xs-12 sidebar-left hidden-xs hidden-sm hidden-md">
        <LeftSideBar page="profile"/>
      </div>
    </div>
  )
 }
});

以及我使用 axios

调用 API 的操作
    export var viewingProfile = (ux,y,z) => {
    return function (dispatch, getState)  {
      return axios.post(root + '/api/getUser', {
          Id: x,
          orgid: y,
          profileId: z
      })
      .then(function (res) {
        if(res.data.statusEntity.statusCode == -201){
          window.location.href = "/user-not-found"
        } else {
          dispatch(updateState(res.data))
          return res.data;

        }
      })
    }
  }

render() 方法中调用调度是错误的做法,因为渲染方法应该是不可变的。使用 redial 提供的钩子包装您的组件以解析数据 api。请参考 redial repo.

中的示例用法

最后,在对 react server-side 渲染中来自另一台服务器的渲染数据进行了大量工作之后,我找到了最佳解决方案。 首先,您不需要重拨,这是多余的,至少对我而言是这样。所以我把它从我的来源中删除了。 然后,我搬家了

dispatch(
 actions.theProfile(x,y,z)
)

到我的路线

export default (store) => {
 const requireApiCall= (nextState, replace, cb) => {
  var state = store.getState();
    return store.dispatch(actions.theProfile(x,y,z)).
    then(()=>{
      cb()
    })

 };
 <Route path="/" component={App}>
   <IndexRoute  component={Home}/>
   <Route path="profile" component={Profile } onEnter={requireApiCall}>
   </Route>
 </Route>
}

调度将执行,路由等待回调 "cb()",当我的 API 呼叫被接听时,我的商店会更新接收到的数据。然后 action 解决承诺,然后 requireApiCall 解决承诺和 returns 回调 "cb()"。路由开始呈现组件,发送给客户端的答案将使用接收到的数据生成。

我知道它有点复杂,但效果很好。