Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reactjs Redux should we create sub reducer for every object in the state tree?

Tags:

redux app as far as i have learned, proper way to maintain your state tree is to normalize it, flaten data as far as possible and use combinereducer to create slices of the state tree.

example App that has posts and users

const rootReducer = combineReducers({
  user:userReducer,
  posts:postsReducer,
});
const store = createStore(rootReducer);

given posts array keep all posts init, State.posts can look like

let initialState =   {
    byId:{1:{id:1,title:'post1'}},
    ids:[1],
    meta_data:{unread:1,old:0}
    }

now if we have around 10,000 posts we would end up with state.post.ids.length === 10000 and this is fine,

Question is. since our reducer returns a new state every time it needs to update for example we need to update the meta_data.unread to be equal 0, we will return a new Post object.

return object.assign({},state,{meta_data:{unread:0,old:1}})

which will re-render all Selectors and components that consume any attribute of state.post tree !

which sound like a problem right ?** all we wanted is updating unread counter.. why recalculate all selectors and components of Posts ?

so i had this idea that may be the state.posts should also be composed used combineReducers so that every attr. of posts should have a reducer it self.

splitting postsReducer into multiple

postsMainReducer, ==> deal with adding or removing posts
postMeta_dataReducer, ==> deal with meta_data of posts
singlePostReducer ==> Now this is dynamic !! how can i create such ??

is this correct ?, i'm adding more complexity than needed ?

-->can someone point show us a picture of an already running enterprise app state tree ? so we can learn how from it how to organize state ?

like image 610
Zalaboza Avatar asked Aug 28 '16 18:08

Zalaboza


2 Answers

It’s important to note that a Redux store really only has a single reducer function. The store passes the current state and dispatched action to that one reducer function, and lets the reducer handle things appropriately.

Obviously, trying to handle every possible action in a single function does not scale well, simply in terms of function size and readability, so it makes sense to split the actual work into separate functions that can be called by the top-level reducer. In particular, the common suggested pattern is to have a separate sub-reducer function that is responsible for managing updates to a particular slice of state at a specific key. The combineReducers() that comes with Redux is one of the many possible ways to achieve this. It’s also highly suggested to keep your store state as flat and as normalized as possible. Ultimately, though, you are in charge of organizing your reducer logic any way you want.

However, even if you happen to have many different independent sub-reducers, and even have deeply nested state, reducer speed is unlikely to be a problem. JavaScript engines are capable of running a very large number of function calls per second, and most of your sub-reducers are probably just using a switch statement and returning the existing state by default in response to most actions.

If you actually are concerned about reducer performance, you can use a utility such as redux-ignore(https://github.com/omnidan/redux-ignore) or reduxr-scoped-reducer(https://github.com/chrisdavies/reduxr-scoped-reducer) to ensure that only certain reducers listen to specific actions. You can also use redux-log-slow-reducers(https://github.com/michaelcontento/redux-log-slow-reducers) to do some performance benchmarking.

Here are projects I most refer to -

Those are actual uses of Redux.

Here's some links:

https://github.com/andrewngu/sound-redux

https://github.com/echenley/react-news

https://github.com/paulhoughton/remember/

https://github.com/paulhoughton/mortgage/

https://github.com/benoitvallon/react-native-nw-react-calculator

https://github.com/jfurrow/flood

https://github.com/FH-Potsdam/shifted-maps

https://github.com/quirinpa/2post

https://github.com/karlguillotte/Ctfs

https://github.com/madou/gw2armory.com

like image 187
Md.Estiak Ahmmed Avatar answered Sep 25 '22 16:09

Md.Estiak Ahmmed


which will re-render all Selectors and components that consume any attribute of state.post tree !

That is not a certainty, components do not have to re-render when unrelated properties of the store change. If your component is a PureComponent, it will not be re-rendered when the reference of the properties (values for primitive props) are equal to what they were the last render.

var foo = { a: [1, 2, 3], someCount: 1 };
var bar = Object.assign({}, foo, { someCount: 2 });
console.log(foo.a === bar.a);
// true

The returned state in the reducer will indeed be a new object, but your posts array will still be the same array (equal reference) after the assign. So React handles this nicely if you use PureComponent.

What you might stumble on next: If you are using derived data derived of the store or multiple stores in a selector, you can use reselect to memoize the result so that the reference stays the same - no re-render needed. An article describes the problem with creating new arrays for derived or empty data in each render cycle.

like image 24
oldwizard Avatar answered Sep 26 '22 16:09

oldwizard