Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding combineReducers

Tags:

reactjs

redux

New to react and redux so playing around with some very simple code to see how it all works. When I try passing in a combineReducers method to a redux store then I get an error. If I remove the combinedReducers and pass the reducer in directly to the store all works fine.

let store = createStore(rootReducer);

Error

Uncaught Error: Objects are not valid as a React child (found: object with keys {reducer}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of App.

Why do I get an error when I use combineReducers ? What if I wanted to add more reducers I presume thats what combineReducers is there for ?

main.js

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

import App from './components/app';

let reducer = (state=0, action) => {
  switch (action.type) {
    case 'INCREASE': 
            return state+1
    case 'DECREASE': 
            return state-1
    default: return state
  }
}

const rootReducer = combineReducers({
    reducer:reducer
});

let store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , document.querySelector('.container'));

//app.js

import React, { Component } from 'react';
import {connect} from 'react-redux';

class App extends Component {
  render() {
        let {number, increase, decrease} = this.props
        return(
                <div>
              <div>{number}</div>
              <button onClick={e=>increase()}>+</button>
              <button onClick={e=>decrease()}> - </button>
            </div>
            );
     }
}

let mapStateToProps = state => ({
  number: state
})

let mapDispatchToProps = dispatch => ({
  increase: () => dispatch({type: 'INCREASE'}),
  decrease: () => dispatch({type: 'DECREASE'})
});

export default connect(mapStateToProps, mapDispatchToProps)(App);
like image 278
me-me Avatar asked Dec 06 '16 02:12

me-me


People also ask

Why do we need multiple reducers?

Multiple slice reducers can respond to the same action, independently update their own slice as needed, and the updated slices are combined into the new state object. Because this pattern is so common, Redux provides the combineReducers utility to implement that behavior.

How do you merge two reducers in Redux?

To manage the multiple reducers we have function called combineReducers in the redux. This basically helps to combine multiple reducers into a single unit and use them. Example: Let's make a simple book list application with help of redux to understand to combine multiple reducers.


2 Answers

Combine reducers takes a hash of reducers and returns a reducer. The resulting reducer represents an object of the same shape as the hash.

So, a call like this:

combineReducers({ name: nameReducer})

Would produce a state object that might look something like this:

{ name: 'Joe Shmo' }

In your example, you are producing a global state tree that looks like this:

{ reducer: 0 }

But you are trying to pull a property called number out of this in your mapStateToProps.

If you change your reducer declaration to look like this:

const number = (state=0, action) => {
  switch (action.type) {
    case 'INCREASE': 
            return state+1
    case 'DECREASE': 
            return state-1
    default: return state
  }
}
const rootReducer = combineReducers({
    number
});

Then change your mapStateToProps to look like this:

const mapStateToProps = ({number}) => ({number});

Your code should start working.

like image 108
Christopher Davies Avatar answered Dec 02 '22 06:12

Christopher Davies


https://redux.js.org/docs/basics/Reducers.html

import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

Note that this is equivalent to:

export default function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

You could also give them different keys, or call functions differently. These two ways to write a combined reducer are equivalent:

const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
})
function reducer(state = {}, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  }
}

And don't forget connect each parts

@connect(state => ({
  reducerName: state[partStoreName]
}))
like image 44
zloctb Avatar answered Dec 02 '22 07:12

zloctb