Hao
Hao

Reputation: 6609

Should the state returned by reducers follow the same shape?

https://medium.com/front-end-hacking/an-intro-to-ngrx-effects-ngrx-store-with-angular-4-c55c4d1d5baf

https://github.com/aravindfz/ngrx-store-demo ( code )

Based on this tutorial, I am confused with the state reducers need to return. I always thought the reducer should be app-wide reducer to return the next application state. But this tutorial has this authorFilter reducer which return a state which is used to do filtering.

// reducer: authorFilter
export const authorFilter = (state = blogs => blogs, action: any) => {
    switch (action.payload && action.payload.type) {
        case 'All': {
            return blogs => blogs;
        }
        case 'Other': {
            return blogs => blogs.author === action.payload.value;
        }
        default: {
            return state;
        }
    }
};

When the main reducer is

// reducer: blog
export const blog = (state: any = [], action) => {
    switch (action.type) {
        case 'ADD_BLOG_SUCCESS': {
            return { data: [...state.data, action.payload] }
        }
        case 'LOAD_BLOGS_SUCCESS': {
            return { ...state, data: action.payload }
        }
        case 'DELETE_BLOG_SUCCESS': {
            return {
                data: state.data.filter(blog => {
                    return blog.id !== action.payload.id;
                })
            };
        }
        default: {
            return state;
        }
    }
};

Then in main component

  ngOnInit() {
    this.blogs$ = Observable.combineLatest(
      this.store.select('blog'),
      this.store.select('authorFilter'),
      (blogs: any, authorFilter: any) => {
        return blogs.data ? blogs.data.filter(authorFilter) : [];
      }
    );

    this.loadBlogs();
  }

  ngOnChanges() {
  }

  loadBlogs() {
    this.store.dispatch(this.blogActions.loadBlogs('All'));
  }

Since different state returned in different reducer ( blog and authorFilter), should I consider then as different store? I thought the state if the application state which share the same shape..

Upvotes: 0

Views: 61

Answers (1)

Brendan Whiting
Brendan Whiting

Reputation: 504

For a simple app you're fine to just have one reducer, but for a larger more complex app it's a good idea to break down your state into pieces and then compose them together. So each reducer is just responsible for it's piece of state, and then there's a function that composes the reducers together.

Here's a little example that uses the pattern I'm more used to and is more explicit about how it works. At the end of the day, you still only have one central state object.

// Part One

export interface PartOneState {
    a: string,
};

export const partOneInitialState: PartOneState = {
    a: '',
};

export const SAY_HI = 'SAY_HI';
export class SayHi implements Action {
    readonly type = SAY_HI;
}

export const SAY_BYE = 'SAY_BYE';
export class SayBye implements Action {
    readonly type = SAY_BYE;
}

export const partOneReducer = (state: PartOneState = partOneInitialState,
                               action): PartOneState => {
    switch (action.type) {
        case SAY_HI:
            return {  a: 'hi' };
        case SAY_BYE:
            return {  a: 'bye' };

        default:
            return state;
    }
}

// Part Two

export interface PartTwoState {
    b: number;
};

export const partTwoInitialState: PartTwoState = {
    b: 0,
};

export const ADD = 'ADD';
export class Add implements Action {
    readonly type = ADD;
}

export const SUBTRACT = 'SUBTRACT';
export class Subtract implements Action {
    readonly type = SUBTRACT;
}

export const partTwoReducer = (state: PartTwoState = partTwoInitialState,
                               action): PartTwoState => {
    switch (action.type) {
        case ADD:
            return {  b: state.b + 1 };
        case SUBTRACT:
            return {  b: state.b - 1 };

        default:
            return state;
    }
}

// Compose them together


export interface AppState {
    partOne: PartOneState,
    partTwo: PartTwoState
}

export const reducer: ActionReducerMap<AppState> = {
    partOne: partOneReducer,
    partTwo: partTwoReducer
};



// In your app.module.ts file...
{
    // ...
    imports: [
        // ...
        StoreModule.forRoot(reducer),
        // ...
    ]
}
`

Upvotes: 1

Related Questions