reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.88k stars 15.27k forks source link

Code splitting and reducer composition #346

Closed jquense closed 9 years ago

jquense commented 9 years ago

Here is the simplest setup I can think of to try and get the issue across. Let me know what's confusing or unclear and I will try and clarify as much as I can. It may also be helpful for the sake of bikeshedding this particular issue to assume that I can't change the server responses.

https://gist.github.com/jquense/67ba7b7ca247b1626980

The situation is that in my app I need the siteStatusReducer state everywhere in the application. It contains a bunch of general user and app state, that is aggregated on the server. In this example we are looking at currentStatus which is the top most status in a stack of statuses. There is only one page in the app where you can actually change a status (where the siteStatusesReducer is need). Which means that the siteState.currentStatus is mostly static, unless you are on the status changing page, in which case, it needs to be updated if a new status is added that is "more recent" than the current one.

The redux suggested way of dealing with this is to combine these two reducers into a single one that manages both sets of state. That doesn't seem optimal for a few reasons.

Since siteState is used everywhere, it means that siteStatuses needs to be loaded everywhere as well, even tho it contains code that is irrelevant and unused in 90% of the application. Even accepting that as inevitable, it gets even worse when siteState contains a bunch of these aggregations; say 5 fields, analogous to currentStatus, that are only "dynamic" in a small section of the app.

I am sure there is a clever way of conditionally combining the stores if they are loaded, but I am unsure what that would look like and how it would complicate selecting that state for a view.

thanks for taking a look :)

jquense commented 9 years ago

Willing to admit that this isn't the right way to go but I did manage to solve this just by implementing waitFor for anyone who is interested.

https://gist.github.com/jquense/c9d2d82d3a7f5aa441be

Still open for a more idiomatic solution ;)

gaearon commented 9 years ago

LOL that's pretty nice.

wmertens commented 9 years ago

Cute waitFor :smile:.

Another way to solve this is to make the hidden state explicit, namely the latest status has the highest timestamp.

So you would send actions containing statuses and timestamps as determined by the server, and your reducers would create a sorted status array if you want it, and would calculate the newest status that passed through so far (replace the currentStatus if a status has a newer timestamp). Then it is up to the server to send the most recent status as early as possible.

wmertens commented 9 years ago

Upon rereading this I notice that I completely missed the point :sweat_smile:.

Yeah, it's sort of a waste to have unused code loaded and doing nothing. A better example may be some huge third-party component that has lots of state and that gets loaded dynamically, with a reducer that should be processing actions too. Then indeed it would be nice if the reducer only gets added when needed.

However, reducers are generally small, so not sure this is a great concern...

On a somewhat related note, suppose you have a component with actionCreators that send some special sort of promised and require a special middleware, wouldn't it be possible to wrap dispatch in some way? Not sure if you'd be able to pass it via Context still.

If that works, perhaps a similar thing can be done with reducers, creating a wrapping store that extends the regular store with new reducers.

gaearon commented 9 years ago

Let's move the discussion into #350, it consolidates similar issues.