Umbro
Umbro

Reputation: 2204

Cannot read property 'data' of undefined in reducer redux

I have such a problem in reducers to todos trying to assigntodos: action.payload.data. I have error:

Cannot read property 'data' of undefined error.

const rootReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'GET_TODOS':
          return {
            ...state,
            todos: action.payload.data
          };

        default:
          return state;
      }
    };

Demo here: https://stackblitz.com/edit/react-ewpquh

actions

import axios from 'axios';

export const GET_TODOS = 'GET_TODOS';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAILURE = 'FETCH_FAILURE';

export const getTodos = () => 
dispatch => {
  dispatch({type: GET_TODOS});

  return axios({
      url: 'https://jsonplaceholder.typicode.com/todos',
      method: 'GET',
    })
    .then(data => {
      console.log(data);

      dispatch({type: FETCH_SUCCESS, payload:{
        data 
      }});

    })
    .catch(error => {
      console.log(error);

      dispatch({type: FETCH_FAILURE})
    });
};

Todos

import React, { Component } from 'react';
import { connect } from 'react-redux';
import {getTodos} from '../.././actions';

class Todos extends Component {
  componentDidMount() {
    this.props.getTodos(); 
  }

  render() {
    return (
      <ul>
        {this.props.todos.map(todo => {
        return <li key={todo.id}>
                  {todo.title}
               </li>
         })}
      </ul>
    );
  }
}

const mapStateToProps = state => {
  console.log(state.todos);
  const { todos } = state;

  return {
    todos
  };
};

const mapDispatchToProps = dispatch => ({
  getTodos: () => dispatch(getTodos())
});

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

reducers

import {GET_TODOS} from '../../actions';

const initialState = {
  todos: []
};

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'GET_TODOS':
      return {
        ...state,
        todos: action.payload.data
      };

    default:
      return state;
  }
};

export default rootReducer;

store

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

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;

Upvotes: 0

Views: 1922

Answers (5)

SuleymanSah
SuleymanSah

Reputation: 17858

Your actions/index.js file should be:

import axios from 'axios';

export const GET_TODOS = 'GET_TODOS';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAILURE = 'FETCH_FAILURE';

export const getTodos = () => 
 dispatch => {
  dispatch({type: GET_TODOS});

   axios({
      url: 'https://jsonplaceholder.typicode.com/todos',
      method: 'GET',
    })
    .then(response => {
      console.log(response.data);
      const data = response.data;
      dispatch({type: FETCH_SUCCESS, payload:{
        data 
      }});     
    })
    .catch(error => {
      console.log(error);
      dispatch({type: FETCH_FAILURE})
    });
};

And reducers/index.js file should be:

import {GET_TODOS} from '../../actions';

const initialState = {
  todos: [],
  loading: false,
  error: null
};

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'GET_TODOS':
      return {
        ...state,
        loading: true
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        todos: action.payload.data,
        loading: false
      };
    case 'FETCH_FAILURE':
      return {
        ...state,
         loading: false,
         error: action.error
      };
    default:
      return state;
  }
};

export default rootReducer;

Upvotes: 1

Hasan Adaminejad
Hasan Adaminejad

Reputation: 78

first, you should add case FETCH_SUCCESS to reducer :

 case 'FETCH_SUCCESS':
  return {
    todos: action.payload.data
  };

after that, you need to replace this code on action file:

 .then(result => {
  const data = result.data

  dispatch({type: FETCH_SUCCESS, payload:{
    data
  }});

})

finally, check that TodosList is not empty on component:

return (
  <ul>
    {this.props.todos && this.props.todos.map(todo => {
    return <li key={todo.id}>
              {todo.title}
           </li>
     })}
  </ul>
);

Upvotes: 0

jholodev
jholodev

Reputation: 1

The function 'GET_TODOS' is triggered before the promise is fulfilled and executed without sending the payload, The function 'GET_TODOS' must be executed when the promise is fulfilled to be able to send the payload 'data', the function getTodos would remain following way:

export const getTodos = () => 
   dispatch => {
      return axios({
         url: 'https://jsonplaceholder.typicode.com/todos',
         method: 'GET',
      })
     .then(data => {
       dispatch({type: GET_TODOS, payload : data});
      })
     .catch(error => {
        console.log(error);
       dispatch({type: FETCH_FAILURE})
     });
 };

Upvotes: 0

Afraz Ahmad
Afraz Ahmad

Reputation: 416

check your actions.js line

 dispatch({type: GET_TODOS});

You are not passing any payload with this dispatch, May its FETCH_SUCCESS that needs to be in reducer instead of GET_TODOS

Upvotes: 1

hashbytes
hashbytes

Reputation: 799

The error that is coming is for the GET_TODOS Action Type, Look at your action (index file), you are not passing payload object while dispatching GET_TODOS.

Also, you are passing an action called FETCH_SUCCESS, but you do not have any case for it in your reducer. Did you intend to dispatch GET_TODO's here?

Upvotes: 1

Related Questions