I've been following a great course on how to build a server-side rendered app with React and Redux, but I'm now in a situation that the course doesn't cover and I can't figure out by myself.
Please consider the following component (it's pretty basic, except for the export part at the bottom):
class HomePage extends React.Component {
componentDidMount() {
this.props.fetchHomePageData();
}
handleLoadMoreClick() {
this.props.fetchNextHomePagePosts();
}
render() {
const posts = this.props.posts.homepagePosts;
const featuredProject = this.props.posts.featuredProject;
const featuredNews = this.props.posts.featuredNews;
const banner = this.props.posts.banner;
const data = ( posts && featuredProject && featuredNews && banner );
if( data == undefined ) {
return <Loading />;
}
return(
<div>
<FeaturedProject featuredProject={ featuredProject } />
<FeaturedNews featuredNews={ featuredNews } />
<Banner banner={ banner } />
<PostsList posts={ posts } heading="Recently on FotoRoom" hasSelect={ true } />
<LoadMoreBtn onClick={ this.handleLoadMoreClick.bind( this ) } />
</div>
);
}
}
function mapStateToProps( { posts } ) {
return { posts }
}
export default {
component: connect( mapStateToProps, { fetchHomePageData, fetchNextHomePagePosts } )( HomePage ),
loadData: ( { dispatch } ) => dispatch( fetchHomePageData() )
};
The above works fine: the loadData function makes an API request to fetch some data, which is fed into the component through the mapStateToProps function. But what if I wanted to fire multiple action creators in that same loadData function? The only thing that kind of works is if I write the function like this:
function loadData( store ) {
store.dispatch( fetchFeaturedNews() );
return store.dispatch( fetchHomePageData() );
}
export default {
component: connect( mapStateToProps, { fetchHomePageData, fetchNextHomePagePosts } )( HomePage ),
loadData: loadData
};
but this is not great because I need all data to be returned... Keep in mind that the exported Component ends up in the following route configuration:
const Routes = [
{
...App,
routes: [
{
...HomePage, // Here it is!
path: '/',
exact: true
},
{
...LoginPage,
path: '/login'
},
{
...SinglePostPage,
path: '/:slug'
},
{
...ArchivePage,
path: '/tag/:tag'
},
]
}
];
and here's how the loadData function is used once the component is needed by a certain route:
app.get( '*', ( req, res ) => {
const store = createStore( req );
const fetchedAuthCookie = req.universalCookies.get( authCookie );
const promises = matchRoutes( Routes, req.path ).map( ( { route } ) => {
return route.loadData ? route.loadData( store, req.path, fetchedAuthCookie ) : null;
}).map( promise => {
if( promise ) {
return new Promise( ( resolve, reject ) => {
promise.then( resolve ).catch( resolve );
});
}
});
...
}
Also, here's an example of the actions fired by the action creators. They all return promises:
export const fetchHomePageData = () => async ( dispatch, getState, api ) => {
const posts = await api.get( allPostsEP );
dispatch({
type: 'FETCH_POSTS_LIST',
payload: posts
});
}
and the reducer:
export default ( state = {}, action ) => {
switch( action.type ) {
case 'FETCH_POSTS_LIST':
return {
...state,
homepagePosts: action.payload.data
}
default:
return state;
}
}
loadDatafunction is later used? - Tomasz MularczykfetchHomePageData(); what I want is for the loadData function to return both that and the data fetched byfetchFeaturedNews(). In other words, I want it to return two sets of data rather than just one, so that they can both be mapped over in theapp.get( ... )function that is at the very bottom of my question. Combining them into an array like @TomasMularczyk suggested sounds nice, but I can't seem to make it work... - grazianodev