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

Builder/Factory pattern for better type propagation in TypeScript/Flow #84

Open
leonyu opened this issue Apr 28, 2016 · 5 comments

Comments

@leonyu
Copy link

commented Apr 28, 2016

Current handleActions(reducerMap, ?defaultState) signature doesn't allow the propagation of payload types from actions into reducers, as it is a dictionary type.

A builder or factory pattern like the following would allow for better type propagation

let increment = createAction<void>('INCREMENT');
let decrement = createAction<number>('DECREMENT');

ReducerBuilder()
.addReducer(increment, (state, action) => {})
.addReducer(decrement, (state, action) => {})
.toReducerMap();

The reason being the actions could be typed as Action<void> and Action<number> respectively, and .addReducer can be typed as

function addReducer<TState, TPayload>(action: Action<TPayload>, handler: (state: TState, action: Action<TPayload>)=>{});

This would automated propagate the type into the reducer.

@alexgorbatchev

This comment has been minimized.

Copy link

commented Apr 28, 2016

Could this be made as a separate package?

@leonyu

This comment has been minimized.

Copy link
Author

commented Apr 28, 2016

This would make the package 10x more useful to typed users. It is like 5 lines of code.

Of course my example is using OOP syntax. If you prefer I can offer functional syntax that does the same job.

@alexgorbatchev

This comment has been minimized.

Copy link

commented Apr 28, 2016

I know how you feel, I encountered that issue while working on #85 . My question is, does this have to be part of redux-actions or could we think of a way to make it part of a separate package, eg redux-actions-builder?

@leonyu

This comment has been minimized.

Copy link
Author

commented Apr 28, 2016

I was looking at this package, and it looked promising for my use, but it didn't satisfy my need of propagating types without needing runtime

I already have an implementation of it in my own code:

// usage
const reducer = new ReducerBuilder<AuthState>()
.add(QUERY_AUTHENTICATION, (state, action) => Object.assign({}, state, action.payload))
.build();

// implementation
class ReducerBuilder<TState> {
    private map : { [action: string]: Reducer<TState, any>; };
    constructor() {
        this.map = {}
    }
    add<TPayload>(action: ActionCreator<TPayload>, reducer: Reducer<TState, TPayload>) {
        this.map[action.type] = reducer;
        return this;
    }
    build(): Reducer<TState, any> {
        const mapClone = Object.assign({}, this.map);
        return (state: TState = {} as any, action: Action<any>) => {
            let handler = mapClone[action.type];
            return handler ? handler(state, action) : state;
        }
    }
}

It is trivial to convert the above to less OOP syntax, which would be a lot less verbose. (I used OOP because it would be more familiar to the others on the team)

function builder() {
   /* impl */
   return {
      add(action, reducer) { /* impl */ }
      done() { /* impl */ }
   }
}
@krawaller

This comment has been minimized.

Copy link

commented May 18, 2017

One year late to the party, but: I've used and appreciated @leonyu's setup for a while now (thank you!!). Partly because of how nice it plays with TypeScript, but I also prefer that pattern over the regular one in handleActions.

Not saying it is justified adding it to the package though, I do understand @alexgorbatchev's hesitation.

If anyone like me stumbles across this PR in search of some TypeScript guidance, I go over my implementation of @leonyu's idea here (along with a handleActions comparison).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.