Think of the app's state as a database. I suggest you to use this state shape:
{
entities: {
// List of normalized posts without any nesting. No matter whether they have all fields or not.
posts: {
'1': {
id: '1',
title: 'Post 1',
},
'2': {
id: '2',
title: 'Post 2',
}
},
},
// Ids of posts, which need to displayed.
posts: ['1', '2'],
// Id of full post.
post: '2',
}
First of all, we are creating our normalizr
schemas:
// schemas.js
import { Schema, arrayOf } from 'normalizr';
const POST = new Schema('post');
const POST_ARRAY = arrayOf(POST);
After success response, we are normalizing response data and dispatching the action:
// actions.js/sagas.js
function handlePostsResponse(body) {
dispatch({
type: 'FETCH_POSTS',
payload: normalize(body.result, POST_ARRAY),
});
}
function handleFullPostResponse(body) {
dispatch({
type: 'FETCH_FULL_POST',
payload: normalize(body.result, POST),
});
}
In reducers, we need to create entities
reducer, which will be listening all actions and if it has entities
key in payload, would add this entities to the app state:
// reducers.js
import merge from 'lodash/merge';
function entities(state = {}, action) {
const payload = action.payload;
if (payload && payload.entities) {
return merge({}, state, payload.entities);
}
return state;
}
Also we need to create corresponding reducers to handle FETCH_BOARDS
and FETCH_FULL_BOARD
actions:
// Posts reducer will be storing only posts ids.
function posts(state = [], action) {
switch (action.type) {
case 'FETCH_POSTS':
// Post id is stored in `result` variable of normalizr output.
return [...state, action.payload.result];
default:
return state;
}
}
// Post reducer will be storing current post id.
// Further, you can replace `state` variable by object and store `isFetching` and other variables.
function post(state = null, action) {
switch (action.type) {
case 'FETCH_FULL_POST':
return action.payload.id;
default:
return state;
}
}
merge({}, state, payload.entities);
mutate the state? – Thunderstruck