1
votes

I am trying to initialise a basic store from a root reducer with initial state.

My root reducer

import Entity from "../api/Entity";
import { UPDATE_GROUPING } from "../constants/action-types";
import IAction from "../interfaces/IAction";
import IStoreState from "../interfaces/IStoreState";

const initialState:IStoreState = {
  callsInProgress: false,
  groupingOptions: ["Strategy", "User", "Date"],
  groupingType: "Strategy",
  numberOfCalls: 2,
  items: [new Entity()],
};


const rootReducer = (state = initialState, action: IAction<object>) => {  
  switch (action.type) {
    case UPDATE_GROUPING:
      return { ...state, groupingType: action.payload};
    default:
      return state;
  }  
};

export default rootReducer;

When I create the store with the rootreducer as below

import { createStore } from 'redux';
import rootreducer  from '../reducers/rootreducer';

const store = createStore(rootreducer);
export default store;

It works. The React components get initialised with the correct state for groupingType, groupingOptions etc.

However if I try and use a combineReducers() approach - even with just this single root reducer (should be identical) then when my components load, they do not have any initial state passed.

ie

import { createStore } from 'redux';
import reducers  from '../reducers';

const store = createStore(reducers);
export default store;

My index.ts in the reducers folder which returns a combineReducers() call (the one which doesnt work)

import {combineReducers} from 'redux';
import rootreducer from './rootreducer';

// main reducers
export default combineReducers({
  rootreducer
});

And lastly my component which hooks into redux and should import the state from the redux store

import updateGroupingType from "./actions/uiactions";
import './App.css';
import * as React from 'react';
import { connect } from 'react-redux';
import IStoreState from './interfaces/IStoreState';

interface IGroupingProps {
    groupingOptions? : string[],
    groupingType? : string,
    updateGroupingAction? : any
  }

class GroupingSelector extends React.Component<IGroupingProps, {}> {

    constructor(props: IGroupingProps) {
        super(props);

        this.onGroupingChange = this.onGroupingChange.bind(this);
      }

      public render() {
            if (this.props.groupingOptions == null)
            {
                return null;
            }

                return (
            <div className="Grouping-selector">
                <div className="Horizontal-panel-right Grouping-search-combo">
                    <select onChange={this.onGroupingChange}>
                        {this.props.groupingOptions.map((name, index)=> 
                            <option key={index}>{name}</option>
                        )}
                    </select>
                </div>
                <div className="Content Horizontal-panel-right">
                    Group by
                </div>            
            </div>);
        }

    private onGroupingChange(e: any) {
          const { value } = e.target;
          this.props.updateGroupingAction(value);
      }    
}

const mapStateToProps:any = (state: IStoreState) => {
    return {
        groupingOptions: state.groupingOptions,
        groupingType: state.groupingType,
    };
  }    

const mapDispatchToProps = (dispatch:any) => {
    return {
        updateGroupingAction: (groupingType:string) => dispatch(updateGroupingType(groupingType))
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(GroupingSelector);

Why is my usage of combineReducers not working in the same way as when I use the single rootreducer?

2

2 Answers

2
votes

From the doc

The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.

The resulting reducer calls every child reducer, and gathers their results into a single state object. The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to combineReducers()

When you are using rootReducer as a key inside of your combineReducers, it will create a state which shape will be

{ "rootReducer": YOUR_PREVIOUS_STATE}

You should use combineReducers only if you have different reducers for each key

2
votes

Your root reducer should be key value pairs like,

export default combineReducers({
  home:homeReducer
});

So that in your component, mapStateToProps() you will be able to access these values as,

const mapStateToProps = (state: any) => {
  return {
    users: state.home
  };
};