dphilipson / typescript-fsa-reducers

Fluent syntax for defining typesafe reducers on top of typescript-fsa.
MIT License
220 stars 16 forks source link

How to combine reducers? #23

Closed MJLHThomassen-Eurocom closed 6 years ago

MJLHThomassen-Eurocom commented 6 years ago

Hey there, first of all great library and extensionto typescript-fsa, thanks for this!

I was wondering, is there an easy way to combine multiple reducers easily just like "combineReducers" from redux itself?

Currently I have the following (simplified) setup:

HomePageReducer.ts:

(...)

export interface HomePageState
{
    readonly msRefreshInterval: number;
}

export const InitialHomePageState: HomePageState = {
    msRefreshInterval: 5000
};

export const homePageReducer = reducerWithInitialState(InitialHomePageState)
    .case(homePageActions.setHomePageRefreshInterval, (state, msRefreshInterval) => ({ ...state, msRefreshInterval }))
    .build();

AppReducer.ts:

(...)
import { appActions } from "./AppActions";

import { homePageActions } from "scenes/HomePage/HomePageActions";
import { HomePageState, InitialHomePageState, homePageReducer } from "scenes/HomePage/HomePageReducer";

export interface AppState
{
    readonly loginState: LoginStates;
    readonly username: string;

    readonly homePage: HomePageState;
}

const InitialAppState: AppState = {
    loginState: LoginStates.LoggedOut,
    username: "",

    homePage: InitialHomePageState
};

const appReducers = reducerWithInitialState(InitialAppState)
   .case(appActions.getUsername.started, (state) => (
        {
            ...state,
            loginState: LoginStates.LoggingIn
        }))
    .case(appActions.getUsername.done, (state, { result }) => (
        {
            ...state,
            loginState: LoginStates.LoggedIn,
            username: result
        }))
    .case(appActions.getUsername.failed, (state) => (
        {
            ...state,
            loginState: LoginStates.LoggedOut
        }))
    .build();

export const appReducer = (state: AppState = InitialAppState, action: Action): AppState =>
{
    return {
        ...appReducers(state, action),
        homePage: homePageReducer(state.homePage, action)
    };
};

Is there a way to combine homePageReducer with the root appReducer by chaining it to the appReducer instead of having to .build() both reducers and combine them manually?

dphilipson commented 6 years ago

Hi MJLHThomassen-Eurocom!

There's nothing built into this library to do so, but it sounds like you might be interested in something like https://github.com/acdlite/reduce-reducers, which is useful when you want to apply multiple reducers to the same state object. It's a little awkward since you're "mixing" the levels of reducers in a sense, but you could try something like:

import { combineReducers } from "redux";
import reduceReducers from "reduce-reducers";

// ...

export const appReducer = reduceReducers(
    appReducers,
    combineReducers({ homePage: homePageReducer })
);

Does that do what you want?

MJLHThomassen-Eurocom commented 6 years ago

@dphilipson Thanks for the tips. That does look like something I'm looking for.

I'm new to React and Redux so I'm still playing around a bit seeing whats best for the architecture of my app. Since the app is a webpage with multiple "modules" i split the state between those modules in the way I described. I'm not sure as to wether thats a good thing or not but atleast it keeps things "findable" for me.

dphilipson commented 6 years ago

What you say about splitting by module makes sense to me, it sounds like you're on the right track. Glad I could help!