Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find a way to improve reducers composition #457

Open
dani-mp opened this issue Oct 22, 2020 · 4 comments
Open

Find a way to improve reducers composition #457

dani-mp opened this issue Oct 22, 2020 · 4 comments

Comments

@dani-mp
Copy link
Contributor

dani-mp commented Oct 22, 2020

Currently, when you want to create a store, you need to pass all reducers like this:

let store = AppStore(reducer: { action, state in
            return AppState(
                entities: entitiesReducer(action: action, state: state?.entities),
                player: playerReducer(action: action, state: state?.player),
                details: detailsReducer(action: action, state: state?.details)
            )
        }, state: nil, middleware: [])

It'd be great if we had something like https://redux.js.org/api/combinereducers to make this nicer.

@mjarvis
Copy link
Member

mjarvis commented Oct 22, 2020

The only way I've been able to make a combineReducers type function for ReSwift is by making state non-optional in the reducers, and having each reducer be a root-level reducer (or at least know about a protocol conformed by the root state).

The ability for the js combine works is by using the fact that JS objects are more loose, that they can be constructed partially, built without knowledge of other parts. The type-safety of Swift doesn't seem to lend itself to that ability.

I'm not sure how one could implement this at all without making state non-optional, which in turn brings in a host of other requirements, (like moving default state out of reducers).

To be honest, I'm quite partial to making state in reducers non-optional. It fits much more with Swift type safety.

@DivineDominion
Copy link
Contributor

I'd prefer a non nil state as well, especially since it almost never is nil 👍

In practice, the first thing I do in the main reducer passed to the store is var state = state ?? AppState() -- and henceforth all sub-state reducers can rely on a non-nil state.

From the top of my head, I can think of the Store API to change to:

  1. Make the initializer's reducer expect a non-nil state
  2. Add a initialState, or a "state factory" (@autoclosure), so that users can tell what the initial state should look like if store.state == nil
  3. Do the var state = state ?? initialState dance in the store itself

Once we have knowledge of an initial state, maybe it makes sense to just put in into the Store.state property right away and change its type to a non-nil property.

To be honest, I never understood the utility of ReSwiftInit apart from triggering the first reducer pass (where I would then create the initial state). When the initialState/factory provides a non-optional value anyway, we may get rid of ReSwiftInit as well, I think. -- Do you guys use ReSwiftInit for anything else than initial state setup?

@dani-mp
Copy link
Contributor Author

dani-mp commented Oct 23, 2020

Something that has always puzzled me is the fact that the reducer signature is (Action, Substate?) -> Substate, instead of just (Action, Substate) -> Substate, being Substate either User or User?.

I think we shouldn't assume that a slice of the store must always have a value, you can have a currentUser reducer that returns nil in some scenarios. I think this can be done with the current API but it can get weird with the typing.

Being able to pass the initial state or a function that returns the initial state seems like the right thing to do, but I have to admit that I've always liked about ReSwift that reducers can auto initialize themselves. I would prioritize here a more correct typing, tho.

@DivineDominion
Copy link
Contributor

@dani-mp Do you have made any progress on this or do you have a proposal for e.g. ReSwift 7 that we should discuss?

I would be pro (Action, Substate) -> Substate, for example, because I don't know from experience why you wouldn't be able to initialize the first sub-state value when you create your store instance.

@DivineDominion DivineDominion added this to the 7.0 milestone Feb 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants