Skip to content

Commit 7210d64

Browse files
Experimenting with plain FSA actions instead of redux-actions.
1 parent cce252b commit 7210d64

File tree

8 files changed

+85
-40
lines changed

8 files changed

+85
-40
lines changed

src/store/interfaces.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,27 @@ export interface IAction {
44
type: string;
55
}
66

7+
export interface IPayloadAction<Payload> extends IAction {
8+
payload: Payload;
9+
}
10+
11+
export interface IErrorAction extends IAction {
12+
error: boolean;
13+
}
14+
15+
export interface IFetching {
16+
isFetching?: boolean;
17+
}
18+
719
export interface IThunk {
820
(dispatch: Dispatch): Promise<any>;
921
}
1022

11-
export interface IData {
12-
data: any;
23+
export interface INormalized<Entities, Result> {
24+
entities: Entities;
25+
result: Result;
26+
}
27+
28+
export interface IReducerMap<TState, TAction> {
29+
[action: string]: (state: TState, action: TAction) => TState;
1330
}

src/store/posts/actions.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
import { REQUEST_POSTS, RECEIVE_POSTS, IPost } from './';
2-
import { IAction, IThunk, IData } from '../interfaces';
1+
import { REQUEST_POSTS, RECEIVE_POSTS, IPostsJSON, IPostsAction } from './types';
2+
import { IAction, IThunk, IPayloadAction } from '../interfaces';
33
import fetch from 'isomorphic-fetch';
44

5-
export interface IPostsAction extends IAction {
6-
items: Array<IPost>;
5+
export function requestPosts(): IAction {
6+
return { type: REQUEST_POSTS };
77
}
88

9-
export function requestPosts(): IPostsAction {
10-
return { type: REQUEST_POSTS, items: null };
11-
}
12-
13-
export function receivePosts(json: IData): IPostsAction {
14-
return { type: RECEIVE_POSTS, items: json.data };
9+
export function receivePosts(json: IPostsJSON): IPostsAction {
10+
return { type: RECEIVE_POSTS, payload: json };
1511
}
1612

1713
export function fetchPosts(): IThunk {

src/store/posts/constants.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/store/posts/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
export * from './actions';
2-
export * from './state';
3-
export * from './constants';
2+
export * from './types';
43
export * from './reducer';

src/store/posts/reducer-spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect } from 'chai';
2+
import { postsReducer, fetchPosts, receivePosts, requestPosts } from './';
3+
4+
describe.only('posts-reducer', () => {
5+
// it('sets the initial state', () => expect(postsReducer(undefined, increaseCounter(1))).to.eql({ value: 1 }));
6+
// it('handles INCREASE_COUNTER', () => expect(postsReducer({ value: 10 }, increaseCounter(1))).to.eql({ value: 11 }));
7+
// it('handles DECREASE_COUNTER', () => expect(postsReducer({ value: 10 }, decreaseCounter(1))).to.eql({ value: 9 }));
8+
});

src/store/posts/reducer.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
1-
import {
2-
RECEIVE_POSTS,
3-
REQUEST_POSTS,
4-
IPostsState,
5-
IPostsAction,
6-
} from './';
1+
const { normalize, Schema, arrayOf } = require('normalizr');
2+
import { RECEIVE_POSTS, REQUEST_POSTS, IPostsState, IPostsAction } from './types';
3+
import { IPayloadAction, IReducerMap } from '../interfaces';
74

85
const initialState: IPostsState = {
96
isFetching: false,
10-
items: [],
7+
entities: [],
8+
result: [],
9+
};
10+
11+
const article = new Schema('articles');
12+
const user = new Schema('users');
13+
article.define({ author: user });
14+
15+
const REDUCERS: IReducerMap<IPostsState, IPostsAction> = {
16+
[RECEIVE_POSTS](state: IPostsState, action: IPostsAction): IPostsState {
17+
const { entities, result } = normalize(action.payload.data, arrayOf(article));
18+
return Object.assign({}, state, {
19+
entities,
20+
result,
21+
isFetching: false
22+
});
23+
},
24+
25+
[REQUEST_POSTS](state: IPostsState, action: IPostsAction): IPostsState {
26+
return Object.assign({}, state, { isFetching: true });
27+
},
1128
};
1229

1330
export function postsReducer(state: IPostsState = initialState, action: IPostsAction): IPostsState {
14-
switch (action.type) {
15-
case RECEIVE_POSTS:
16-
return Object.assign({}, state, { items: action.items, isFetching: false });
17-
case REQUEST_POSTS:
18-
return Object.assign({}, state, { isFetching: true });
19-
default:
20-
return state;
21-
}
31+
const reducer = REDUCERS[action.type];
32+
return reducer ? reducer(state, action) : state;
2233
}

src/store/posts/state.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/store/posts/types.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { IAction, IFetching, INormalized, IPayloadAction } from '../interfaces';
2+
3+
export interface IAuthor {
4+
id: string;
5+
name: string;
6+
email: string;
7+
}
8+
9+
export interface IPost {
10+
id: string;
11+
title: string;
12+
body: string;
13+
author: IAuthor | string;
14+
}
15+
16+
export interface IPostsJSON {
17+
data: IPost[];
18+
}
19+
20+
export interface IPostsState extends IFetching, INormalized<IPost[], number[]> { }
21+
22+
export interface IPostsAction extends IPayloadAction<IPostsJSON> { }
23+
24+
export const REQUEST_POSTS: string = 'REQUEST_POSTS';
25+
export const RECEIVE_POSTS: string = 'RECEIVE_POSTS';

0 commit comments

Comments
 (0)