Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: Cannot read property 'map' of undefined in react-redux

Im new to React and Redux and still kinda confused a little bit.

My goal is to render a bunch of json datas in the HTML by using GET request. I'm using react and redux to manage the state of the objects, but I believe my problem is that the data is not even there

so basically whenever someone request a URL /courses , he/she will see bunch of data in json.

I get the error in the component

TypeError: Cannot read property 'map' of undefined

Here's the code

Action

export function getCourses() {
  return (dispatch) => {

    return fetch('/courses', {
      method: 'get',
      headers: { 'Content-Type', 'application/json' },
    }).then((response) => {
      if (response.ok) {
        return response.json().then((json) => {
          dispatch({
            type: 'GET_COURSES',
            courses: json.courses
          });
        })
      }
    });
  }
}

Reducer

export default function course(state={}, action) {

  switch (action.type) {
    case 'GET_COURSES':
      return Object.assign({}, state, {
        courses: action.courses
      })
    default:
      return state;
  }


}

Component

import React from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';


class Course extends React.Component {



  allCourses() {
    return this.props.courses.map((course) => {
      return(
        <li>{ course.name }</li>
      );

    });
  }

  render() {
    return (
      <div>
        <ul>
          { this.allCourses() }
        </ul>

      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    courses: state.courses
  }
}

export default connect(mapStateToProps)(Course);

Index reducer, where i combine everything

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

export default combineReducers({
  course,
});

Configure Store , where i store the intial state and the reducer

import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';

export default function configureStore(initialState) {
  const store = createStore(
    rootReducer,
    initialState,
    compose(
      applyMiddleware(thunk),
      typeof window == 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
    )
  );

  return store;
}

I believe why the data is not there is because i didn't call the action? any help would be appreciated.

like image 421
sinusGob Avatar asked Jul 31 '16 06:07

sinusGob


People also ask

How do you Fix Cannot read property of undefined in React?

The "Cannot read property 'props' of undefined" error occurs when a class method is called without having the correct context bound to the this keyword. To solve the error, define the class method as an arrow function or use the bind method in the classes' constructor method.

What does Cannot read property map of undefined mean?

The "cannot read property 'map' of undefined" error occurs when we call the map() method on an undefined value, most often when the map method is called before the data from an API request has arrived. To solve the error, initialize the value you're mapping over to an empty array.


2 Answers

mapStateToProps takes the root state as an argument (your index reducer, which is also the root reducer), not your course reducer. As far as I can tell this is the structure of your store:

-index <- This is the root reducer
  -course

So to get the courses from that state, in your component:

// state is the state of the root reducer
const mapStateToProps = (state) => {
  return {
    courses: state.course.courses
  }
}

Also, you might consider initialising the state of the course reducer with an empty array of courses, so if you have to render the component before the action is fired, you won't get the error.

const initialState = {
    courses: []
};

export default function course(state= initialState, action) {

    ...

}

Finally, you're not firing the action at all, so you will never actually get the courses, I assume you want them to be retrieved once the Course component is loaded, for that you can use the componentDidMount event in your component.

First of all, you need to map the action to a property of the component

// Make sure you import the action
import { getCourses } from './pathToAction';

...

const mapDispatchToProps = (dispatch) => {
  return {
    onGetCourses: () => dispatch(getCourses())
  };
}

// Connect also with the dispatcher
export default connect(masStateToProps, mapDispatchToProps)(Course);

Now call the onGetCourses property when the component mounts

class Course extends React.Component {

    componentDidMount() {
      this.props.onGetCourses();
    }

    ...

}
like image 147
Marco Scabbiolo Avatar answered Oct 04 '22 03:10

Marco Scabbiolo


its because props sometime can be undefined so you have to write a condtion like this

allCourses() {
     if(this.props.courses){
    return this.props.courses.map((course) => {
      return(
        <li>{ course.name }</li>
      );
    });
   }
   else {
   return []; 
  }
like image 45
abhirathore2006 Avatar answered Oct 04 '22 04:10

abhirathore2006