BayLife BayLife - 6 months ago 22
Javascript Question

Can´t access props after using CombineReducer

My previous React-Redux implementation was working, but after I tried to implement the combineReducer function with seperated files, an error is thrown that I don´t really understand. Hope some of you can help me!

ERROR: Uncaught TypeError: this.props.todos.map is not a function

My Reference for that Code was the Async Example of the Redux-Doc´s. But I stated with another example and the change from each examples are not documented in the doc´s.

The first code I will show, is that I had (working):

MyStore

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import addItem from '../reducers/addItem'

export default function configureStore(preloadedState) {
const store = createStore(
addItem,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)

if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}

return store
}


My Reducer

export default (state = ['Test'], action) => {
switch (action.type){
case 'ADD_ITEM':
//return action.item
return [
...state,
{
id: action.id,
text: action.item
}
]
default:
return state

}
}


Actions

export default function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
id: nextTodoId++,
item
}
}


And the subComponent where the input is finally rendered

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

export default class TodoList extends Component {

render() {
const posts = this.props
const isEmpty = posts.length === 0
return (

<div>
<h3>Meine Aufgaben</h3>
<ul>
{isEmpty
? <h3>Sie haben noch keinen Todo´s angelegt</h3>
: <h3>Ihre Ergebnisse</h3>
}
{this.props.todos.map((todo, i) => <li key={i}>{todo.text} </li>)}
</ul>
</div>
)
}
}
const mapStateToProp = state => ({todos: state})

export default connect (mapStateToProp)(TodoList)


What I have change:

First, I created another Reducers File, called Index where I imported the addItem Reducer and exported the rootReducer:

import {combineReducers} from 'redux'
import addItem from './addItem'
import getItem from './getItem'

const rootReducer = combineReducers({
addItem,
getItem
})

export default rootReducer


After that, I changed the Store to import the rootReducer and put it´s reference in the Store (just the changes to configureStore):

import rootReducer from '../reducers/index'

const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)


I don´t know if that Information is also required, but here is my Container Component:

import React, { Component, PropTypes } from 'react'
import AddTodo from '../components/AddTodo'
import TodoList from '../components/TodoList'
import { connect } from 'react-redux'
import addItem from '../actions/addItem'
import getItems from '../actions/getItems'

class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {text: ''}
}

handleClick(e){
console.log(e);
const {dispatch} = this.props
dispatch(addItem(e));
}

componentDidMount(){
console.log("COMPONENT MOUNT");
const {dispatch} = this.props
// dispatch(getItems())
}

componentWillReceiveProps(nextProps) {
console.log("GETTT IT");
console.log(nextProps)
}

render() {

return (
<div>
< h1 > Hallo </h1>
<AddTodo handleAddItem={this.handleClick}/>
<TodoList/>
</div>
)
}
}

App.propTypes = {
dispatch: PropTypes.func.isRequired
}

function mapStateToProps(state){
return {
AddTodo
}
}

export default connect (mapStateToProps)(App)


I hope this issue is not to basic and someone can help me. Thanks in advance!

Answer

If you inspect your redux state you will see that the following code sets up 2 more keys in the state (addItem and getItem):

const rootReducer = combineReducers({
   addItem,
   getItem
 })

So, now to connect todos you need to one of the 2 new keys. If todos is not defined on those, then you need to add the reducer of todos to the combineReducers call.

So this needs to map to a valid location in state:

const mapStateToProp = state => ({todos: state})