在渲染服务器端之前获取数据

现在我正在发现 Este.js,但我对同构应用程序有一点问题.我不明白如何在使用 renderToString() 渲染服务器端之前进行 api 调用.

Right now I'm discovering Este.js and I have a little issue with isomorphic apps. I don't understand how to make api call before rendering server side with renderToString().

一种解决方案是使用 React Router 在路由器级别获取所有数据.根据顶层路由,我可以预测需要哪些数据,进行 api 调用,然后调用 React.renderToString.

One solution consists in doing all the data fetching at the router level using React Router. Depending on the top level route, I can predict which data will be needed, make the api call, and then call React.renderToString.

很好,但我仍然必须在组件级别和路由器级别声明数据依赖关系.我最终编写了两次相同的代码,但我认为这不是最好的方法.

Great, but I still have to declare the data dependencies at the component level AND in the router level. I end up writing the same code twice, and I don't believe it's the best way to do it.

好的,现在我可以做一些我想做的事.使用 React-Router 和这个 link 我已经能够做到以下几点:

EDIT : Ok, for now I'm able to do somewhat what I want. Using React-Router and this link I've been able to made the following :

给定这个全局应用程序状态,我想在指向/todos 时预取 todos

Giving this global app state, I want to prefetch todos when pointing /todos

{
  auth: {
    data: null,
    form: null
  },
  examples: {
    editable: {
      state: null,
      text: 'Some inline-editable text.'
    }
  },
  i18n: {
    formats: {},
    locales: initialLocale,
    messages: messages[initialLocale]
  },
  pendingActions: {},
  todos: {
    editables: {},
    newTodo: {
      title: ''
    },
    list: [{
      id: 1,
      title: 'first todo yipiyo'
    }]
  },
  users: {
    viewer: null
  }
}

todos.react.js

在 todo 组件中我声明了一个静态函数 fetchData.因为我想在我的 appState 中检索正确的密钥,所以我将 'list' 作为参数传递.感觉很脏.

todos.react.js

In todo component I declare a static function fetchData. Because I want to retrieve the correct key in my appState, I pass 'list' as a param. Feels dirty.

class Todos extends Component {

  static fetchData(){
    return actions.loadAllTodos('list');
  }

  componentDidMount(){
    Todos.fetchData();
  }

  render() {
    ...
  }

}

actions.js

API 调用和其他东西,我将密钥传递给承诺 - 感觉很hacky

actions.js

Api call and stuff, I pass the key to the promise - Feels hacky

export function loadAllTodos(key) {

  const promise = new Promise((resolve, reject) => {

    Api.get()
    .then(res => {
      res.key = key; //hacky time
      resolve(res)
    })
    .catch(err => {
      reject(err);
    })

  });

  return dispatch(loadAllTodos, promise);

}

render.js

router.run((Handler, routerState) => {

  var promise = Promise.all(routerState.routes
        .filter(route => route.handler.fetchData)
        .map(route => {
          return route.handler.fetchData();
        })
      );

  promise.then(resp => {

    console.log(resp);

    //Displays : 
    [ { id: 2, title: 'Im a second todo' },{ id: 3, title: 'I like todo' },
    cursor: 'list' ]

    //Some stuff to add resp to appState, using the correct key, yey iso+api=win
    appState = mergeThisWithMagic(appState, resp);

    const html = preloadAppStateThenRenderHtml(Handler, appState);
    const notFound = routerState.routes.some(route => route.name ===
      'not-found');
    const status = notFound ? 404 : 200;
    res.status(status).send(html);
    resolve();

  });


});

如您所见,我将创建一个函数来使用更新后的 todoList 更新 appState.

As you can see, I'll create a function to update appState with the updated todoList.

所有这些都可以吗?我想得到一些反馈,因为我觉得我正走在一条黑暗的道路上:(.

Is this ok to do all of this ? I would like to have some feedback please, because I feel like I'm going in a dark path :(.

推荐答案

我在我的同构服务器端应用程序中通过将我的 fetchData 函数放在组件的 statics 对象中并使用承诺等到所有数据都返回后再将应用程序呈现为字符串.

I accomplished this in my isomorphic server side app by putting my fetchData functions in the statics object of the component(s) and using promises that wait until all the data is returned before rendering the app to string.

然后你会通过 props 将返回的数据传递给渲染的组件.这个例子对我开发这个应用程序很有帮助.React Router 超级演示.

Then you would pass the returned data down to the rendered component via props. This example was instrumental in my development of this app. React Router Mega Demo.

相关文章