Skip to content
This repository has been archived by the owner on Feb 3, 2020. It is now read-only.

Commit

Permalink
Merge pull request #7 from cinnabarcaracal/patch-2
Browse files Browse the repository at this point in the history
Allow returning arbitrary data from the handleAction function
  • Loading branch information
knpwrs committed Feb 10, 2019
2 parents 3ce8234 + e0bd7c7 commit ee82164
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { createAction, handleAction, reduceReducers } from 'redux-ts-utils';
const increment = createAction<void>('increment');
const decrement = createAction<void>('decrement');
const add = createAction<number>('add');
const override = createAction<number>('override');

// Reducer

Expand All @@ -45,6 +46,9 @@ const reducer = reduceReducers<State>([
handleAction(add, (state, { payload }) => {
state.counter += payload;
}),
handleAction(override, (_, { payload }) => ({
counter: payload,
})),
], initialState);

// Store
Expand Down Expand Up @@ -142,6 +146,9 @@ object. [`immer`] will also provide you with a mapped type (`Draft`) of your
state with all `readonly` modifiers removed (it will also remove `Readonly`
mapped types and convert `ReadonlyArray`s to standard arrays).

If your mutation function returns a value other than `undefined`, and does not mutate the
incoming state object, that return value will become the new state instead.

### `reduceReducers<S>(reducers: Reducer[], initialState?: S)`

The `reduceReducers` function takes an array of reducer functions and an
Expand Down
4 changes: 4 additions & 0 deletions src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createAction, handleAction, reduceReducers } from '.';
const increment = createAction<void>('increment');
const decrement = createAction<void>('decrement');
const add = createAction<number>('add');
const override = createAction<number>('override');

// Reducer

Expand All @@ -29,6 +30,9 @@ const reducer = reduceReducers<State>([
handleAction(add, (state, { payload }) => {
state.counter += payload;
}),
handleAction(override, (_, { payload }) => ({
counter: payload,
})),
], initialState);

// Store
Expand Down
32 changes: 32 additions & 0 deletions src/handle-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,35 @@ test('handles specific action with payload', () => {
expect(newState2).toEqual({ counter: 0 });
expect(newState2).toBe(state);
});

test('handles specific action with payload by returning value directly', () => {
const ac1 = createAction<{ num: number }>('foo3');
const state: { readonly counter: number } = { counter: 0 };
const re = handleAction<typeof state>(ac1, (draft, { payload }) => ({
counter: draft.counter + payload.num,
}), state);
const newState1 = re(state, ac1({ num: 7 }));
expect(newState1).toEqual({ counter: 7 });
expect(newState1).not.toBe(state);
});

test('handles specific action with payload and ignores directly returned value if draft is mutated', () => {
const ac1 = createAction<{ num: number }>('foo4');
const state: { readonly counter: number } = { counter: 0 };
const re = handleAction<typeof state>(ac1, (draft, { payload }) => {
draft.counter += payload.num;
return 'unintended return value';
}, state);
const newState1 = re(state, ac1({ num: 10 }));
expect(newState1).toEqual({ counter: 10 });
expect(newState1).not.toBe(state);
});

test('handles specific action and uses previous state if directly return value is undefined', () => {
const ac1 = createAction<void>('foo5');
const state: { readonly baz: number } = { baz: 0 };
const re = handleAction<typeof state>(ac1, () => undefined, state);
const newState1 = re(state, ac1());
expect(newState1).toEqual({ baz: 0 });
expect(newState1).toBe(state);
});
10 changes: 8 additions & 2 deletions src/handle-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ export default function handleAction<S, AC extends TsActionCreator<any> = any>(
return (state: S | undefined, action: ReturnType<AC>) => {
if (action.type === ac.type && state) {
const draft = createDraft(state);
re(draft, action);
return finishDraft(draft);
const reResult = re(draft, action);
const finishedDraft = finishDraft(draft);

if (finishedDraft === state && reResult !== undefined) {
return reResult;
}

return finishedDraft;
}
return (state || s) as any;
};
Expand Down

0 comments on commit ee82164

Please sign in to comment.