// State Tree itself should be normalized and lean towards flat (sort of like a traditional relational database)
//
{
// Route Parameters
params: {
pageId: 1,
},
// Card Reducer
cards: {
1: {
...
},
2: {
...
}
},
// Page Reducer
pages: {
1: {
title: 'Some Page',
cards: new Set(1, 2) // not JSON but important to leverage set for guaranteed uniqueness
}
}
}
You then use what is called a selector
to render a page, or component, etc. The selector can be what you need for the whole UI or just a given component.
const selectCurrentPage = (state) => {
const page = state.pages[state.params.pageId];
return {
...page,
cards: page.cards.map((cardId) => state.cards[cardId])
}
};
To create efficient selectors you can use memoization, see: https://github.com/reactjs/reselect (not coupled to React)
// In this case the work of merging & mapping would be done once, no matter how many re-renders you caused. Truth is though in this case we are just passing around references which is exceptionally fast.
// This gives you a place to do efficient computations & derived data without a messing up your state tree.
// Important to use a memo with a cache size of 1
const selectCurrentPage = createSelector(
selectPageByCurrentId,
(page) => ({
...page,
cards: page.cards.map((cardId) => state.cards[cardId])
})
);
Something really dope about this pattern is that we return the same reference if nothing has changed and a new reference if something has changed. This means our UI framework can use reference semantics to know when to re-render instead of deep comparisons.
So instead of deepEqual(one, two)
our UI tool can say one !== two
and know when to re-render. One of the many benefits of immutability.
Actions creators the things that provide actions for the tree can be effecient by using the tree's existing state, this is useful to avoid duplicate requests/work if something already exists in the tree
const getPage = (id) => ((dispatch, getState) => {
if (selectPageByCurrentId() !== null) return null; // We can short circuit since we have this data, we can be smart about how we do this to based on semantics around the data itself.
fetch(`/page/${id}`)
.then((response) => {
dispatch({
type: 'PAGE_LOAD',
paylod: response.json()
});
}):
})
Your reselect selector body has a reference to
state
in it. Add a dependency to(state) => state.cards
to the selector.