redux-api-middleware
redux-api-middleware copied to clipboard
API requests and server-side rendering
Hello people,
I am currently looking at how I could implement server-side rendering and I was wondering how redux-api-middleware
would play out.
It is very new to me, but If i understand correctly, one way to achieve it is to use async actions via thunks, e.g. redux-thunk
, and wait for these actions to build the redux state before serving it to render the html page.
Is it possible to achieve server-side rendering using redux-api-middleware
to fetch data ? How would you do it ?
Thank you,
Hey, this is actually possible. You can have an implementation like it is done in https://github.com/caljrimmer/isomorphic-redux-app
First define a static property for your component where you define all actions that need to be executed before the site is rendered.
export class TestComponent extends Component {
static need = [
loadData
]
render() {
// rendering code
}
}
Then before you render the page make sure that all promises in need
are resolved:
match({routes, location: req.url}, (err, redirect, props) => {
preRenderMiddleware(
store.dispatch,
props.components,
props.params
).then(() => {
const initialState = store.getState();
const componentHTML = renderToString(
<Provider store={store}>
<RouterContext {...props} />
</Provider>
);
// create html
res.status(200).send(html);
})
});
With preRenderMiddleware
export default function preRenderMiddleware(dispatch, components) {
const needs = components.reduce((prev, current) => {
if (!current) {
return prev
}
const need = 'need' in current ? current.need : []
const wrappedNeed = 'WrappedComponent' in current &&
'need' in current.WrappedComponent ? current.WrappedComponent.need : []
return prev.concat(need, wrappedNeed)
}, [])
const promises = needs.map(need => dispatch(need()))
return Promise.all(promises)
}
@cevou that project doesn't use redux-api-middleware
so it's probably not a good example. It uses redux-promise
to manage promises in a totally different way from this middlware.
@amangeot based on my analysis, redux-api-middleware
never surfaces the promise representing the request made to the API. You can see here that it does an await
on the promise, waiting until it is resolved: https://github.com/agraboso/redux-api-middleware/blob/4e349645f594cdcc2a5d6d38efdfad36e6a4c86f/src/middleware.js#L103
And that means you won't be able to wait for the completion of a .dispatch()
of your 'call API' action, and therefore server rendering really can't be done. You won't know when the API is complete so you can render to string and return the response to the user. Perhaps time for a fork?
@scottnonnenberg It actually works in my project with the implementation shown above. The referenced project was just an example for the static array.
@cevou Ah, thanks. Seems I'm still wrapping my head around async
/await
. Certainly is confusing when combined with next()
callbacks. Looks like it will work, after all, @amangeot, since you can wait for your dispatch()
calls to resolve.
@cevou , how are you able to pass params like url to the action in the need array?
Found it, you need to pass params to the need aswell
/**
* @description
* This function checks if a component has the static need array.
* If it does it will make sure all of the actions listed there are fired before the page is rendered.
*
* @param dispatch
* @param components
* @param params
* @returns {*}
*/
preRenderMiddleware(dispatch, components, params) {
const needs = components.reduce((prev, current) => {
if (!current) {
return prev
}
const need = 'need' in current ? current.need : [];
const wrappedNeed = 'WrappedComponent' in current &&
'need' in current.WrappedComponent ? current.WrappedComponent.need : [];
return prev.concat(need, wrappedNeed)
}, []);
const promises = needs.map(need => dispatch(need(params)));
return Promise.all(promises)
}
Anyone able use redux-api-middleware with SSR? Have an example project?