## Tree ```javascript // 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 } } } ``` ## Selectors 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. ```javascript 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) ```javascript // 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. ## Action Creators 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 ```javascript 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() }); }): }) ```