Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux reducers initializing same state key

I'm messing around with the 'simplest-redux-example' on github and I've added a second reducer that decrements state.count. If I have the increment and decrement reducers in a switch case statement, it works fine. The exercise I wanted to perform was to split reducers into as many modular pieces as possible. This code throws an error saying that count is undefined.

import React from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';

// React component
class Counter extends React.Component {
  render(){
    const { value, onIncreaseClick, onDecreaseClick } = this.props;
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
        <button onClick={onDecreaseClick}>Decrease</button>
      </div>
    );
  }
}

// Action:
const increaseAction = {type: 'increase'};
const decreaseAction = {type: 'decrease'};

// Reducer:
function counter(state, action) {
  let count = state.count;
  switch(action.type){
    case 'increase':
      return {count: count+1};
    default:
      return state;
  }
}
function decrementer(state, action) {
  let count = state.count;
  switch(action.type){
    case 'decrease':
      return {count: count -1};
    default:
      return state;
  }
}
const rootReducer = combineReducers({
  counter,
  decrementer
})

// Store:
let store = createStore(rootReducer, {count: 0});

// Map Redux state to component props
function mapStateToProps(state)  {
  console.log("mapStatetoProps heyyyy");
  return {
    value: state.count
  };
}

// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
  console.log("mapDispatchtoProps heyyyy");
  return {
    onIncreaseClick: () => dispatch(increaseAction),
    onDecreaseClick: () => dispatch(decreaseAction)
  };
}

// Connected Component:
let App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);

React.render(
  <Provider store={store}>
    {() => <App />}
  </Provider>,
  document.getElementById('root')
);
like image 686
tells Avatar asked Sep 20 '15 01:09

tells


People also ask

How do I set reducer to initial state?

There are two main ways to initialize state for your application. The createStore method can accept an optional preloadedState value as its second argument. Reducers can also specify an initial value by looking for an incoming state argument that is undefined , and returning the value they'd like to use as a default.

How do I change the initial state in Redux Reducer?

You can set it at the reducers . Reducers can also set initialState by looking at the incoming state argument (which would be undefined if createStore is not called with initialState ) and returning the values they would like to use as default.

Can Redux have multiple reducers?

Redux has the pattern of a single store principle to maintain one immutable store; however, it also has the concept of multiple reducers. The multiple reducers concept is to handle single functionality per reducer.


2 Answers

The reducers passed to combineReducers get different pieces of the state object.

The resulting reducer calls every child reducer, and gather their results into a single state object. The shape of the state object matches the keys of the passed reducers.

(Emphasis mine)

So, the internal state object would look like

{
  counter: result of passing `state.counter` into counter reducer
  decrementer: result of passing `state.decrementer` into decrementer reducer
}

This is analogous to having separate stores in a flux application, where each store both operates its own "piece" of the global app state.

Since you actually want these two reducers to work on the same portion of the state object, you actually want something more like reduce-reducers, though it's very easy to implement yourself—just pass the state into each reducer in turn, reducing the initial state with the new state from each reducer.

In fact, it's so simple, the implementation is just a few lines:

export default function reduceReducers(...reducers) {
  return (previous, current) =>
    reducers.reduce(
      (p, r) => r(p, current),
      previous
    );
}

and your rootReducer would be

const rootReducer = reduceReducers(counter, decrementer);
like image 113
Michelle Tilley Avatar answered Oct 24 '22 09:10

Michelle Tilley


You're so close! The catch is that when you use combineReducers it actually splits the "state" such that the states of the reducers you feed in are properties on the "state" object.

As such, in order to feed them default parameters as follows: let store = createStore(rootReducer, {counter: {count: 0}, decrementer: {count:0}});

like image 36
SirRodge Avatar answered Oct 24 '22 09:10

SirRodge