Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to merge multiple reducers inside one state?

I'm trying to build an app that has two or more reducers "answering" by one object inside my storeState.

Example:

storeState {
    app: {...},
    comments: [ ...two or more states inside one... ]
}

I already tried the following using combineReducers that didn't work, my comments state became an empty object.

import { combineReducers } from 'redux';
import commentFacebookReducer from './twitch';
import commentYoutubeReducer from './reddit';
import appReducer from './app';

const rootReducer = combineReducers({
  comments: [commentFacebookReducer, commentYoutubeReducer],
  app: appReducer
});

export default rootReducer;

And I already tried to combine my comment reducers before combine them in rootReducer:

import { combineReducers } from 'redux';
import commentFacebookReducer from './twitch';
import commentYoutubeReducer from './reddit';
import appReducer from './app';

const commentReducers = combineReducers({
  commentFacebookReducer,
  commentYoutubeReducer
});

const rootReducer = combineReducers({
  comments: commentReducers,
  app: appReducer
});

export default rootReducer;

But it gives me a storeState like the following. But I need a state that is an array of comments, I can't deal with these reducers names (like commentFacebookReducer or commentYoutubeReducer), because I'll have thousands of reducers like these.

storeState {
    app: {...},
    comments: {
        commentFacebookReducer: {...},
        commentYoutubeReducer: {...}
    }
}
like image 293
Raphael Parreira Avatar asked Aug 19 '17 08:08

Raphael Parreira


2 Answers

I dont think you can do that by just using combineReducer. Because combineReducer expects an object and each property of that object should be a function. Then each function in that object makes an individual property in redux state tree and the value of that property is the value returned by that function (When any action is dispatched).

Also you cannot access one reducer's state in other reducer, because redux only pass that particular reducer's state to the function. That means you cannot get/alter state of commentFacebookReducer inside commentYoutubeReducer.

The only better solution i can think of right now is that you combine them when you are using them (most probably in mapStateToProps). You keep your reducer as you are doing like this:

const commentReducers = combineReducers({
  commentFacebookReducer,
  commentYoutubeReducer
});

const rootReducer = combineReducers({
  comments: commentReducers,
  app: appReducer
});

Now all our comments are in a single object i.e.(state.comments). Now in mapStateToProp you do something like this to combine them in single object::

const mapStateToProps = state => {
    return {
        comments: Object.keys(state.comments).reduce((d, k) => {return d.concat(a[k])}, [] )
    };
};

Now in component, you can simply access them as single array using this.props.comments

like image 144
Prakash Sharma Avatar answered Oct 19 '22 07:10

Prakash Sharma


Considering that what you want to do is multiple reducers for the properties inside each comment, something like:

 comments: [ array of Comment]

Then you shouldn't combine the reducers when you are creating your store, what you could do is create only one reducer for the comments:

comments: commentsReducer

Then the commentsReducer will look like this:

import {commentYouTubeReducer} from 'X'
import {commentFacebookReducer} from 'Y'

const commentsReducer = (state = [], action) => {

    switch (action.type){
       case 'ADD_COMMENT':
         return [..state, 
                {
                  YouTubeComment: commentYouTubeReducer(undefined, action),
                  FacebookComment: commentFacebookReducer(undefined, action)
                }]

    }     
} 

I know this is pretty simple example but I guess that you get my idea. What you do is to compose the commentsReducer inside of it but not as part of the creation of your store. For the cases of an update what you'd pass as state for the commentYoutubeReducer would be its previous value for instance:

 ...
 case 'UPDATE_COMMENT':
 ...
 { youtubeComment: commentYoutubeReducer(comment.youtubeCommnet, action) ... }

Basically your problem is a generalization of the todo list that you can find here: https://codepen.io/haakenlid/pen/qZOVjV

like image 38
Santiago Benitez Avatar answered Oct 19 '22 07:10

Santiago Benitez