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().
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.
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 :
Giving this global app state, I want to prefetch todos when pointing /todos
initialstate.js
{
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
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 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();
});
});
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 :(.
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.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.