0

I have this error while using Redux:

"Uncaught TypeError: Cannot read property 'props' of undefined"

When I didn't use the fetch method in project everything worked. Then I added getting datas (notes) from another site to the project and everything stopped working

My reducer:

 export function itemsHasErrored(state = false, action) {
  if (action.type === 'ITEMS_HAS_ERRORED') {
    return action.hasErrored;
  } else {
    return state;
  }
}

export function itemsIsLoading(state = false, action) {
  if (action.type === 'ITEMS_IS_LOADING') {
    return action.isLoading;
  } else {
    return state;
  }
}

export function getNotesReducer(state = [], action) {
  if (action.type === 'ITEMS_FETCH_DATA_SUCCESS') {
    return action.notes;
  } else {
    return state;
  }
}

My actions:

export function itemsHasErrored(bool) {
  return {
    type: 'ITEMS_HAS_ERRORED',
    hasErrored: bool,
  }
}

export function itemsIsLoading(bool) {
  return {
    type: 'ITEMS_IS_LOADING',
    isLoading: bool,
  }
}

export function itemsFetchDataSuccess(notes) {
  return {
    type: 'ITEMS_FETCH_DATA_SUCCESS',
    notes,
  }
}

export function itemsFetchData(url) {
  return (dispatch) => {
    dispatch(itemsIsLoading(true));

    fetch(url)
        .then((response) => {
          if (!response.ok)
            throw Error(response.statusText);
          dispatch(itemsIsLoading(false));
          return response;
        })
        .then((response) => response.json())
        .then((notes) => dispatch(itemsFetchDataSuccess(notes)))
        .catch(() => dispatch(itemsHasErrored(true)));
  };
}

My store:

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

export default function configureStore(initialState) {
  return createStore(
      rootReducer,
      initialState,
      applyMiddleware(thunk),
  )
}

My component using store:

import React, {Component} from "react";
import NoteCard from "../components/NoteCard";
import "../styles/notes-container.scss";
import { connect } from "react-redux";
import { itemsFetchData } from "../actions/getNotes";
import 'react-uuid'

class NotesContainer extends Component {
  componentDidMount() {
    this.props.fetchData('http://private-9aad-note10.apiary-mock.com/notes');
    console.log('NotesContainer mounted');
  }


  render() {
    function renderCards() {
      return this.props.notes.map(note => {
        return (
            <NoteCard
                id={note.id}
                name={note.title}
            />
        );
      });
    }

    return (
        <div className="notes-container">{renderCards()}</div>
    )
  }
}

const mapStateToProps = (state) => ({
  notes: state.notes
});

const mapDispatchToProps = (dispatch) => {
  return {
    fetchData: (url) => dispatch(itemsFetchData(url))
  }
};

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

2 Answers2

2

The error you give isn't actually related to redux. Its because your function doesn't have the correct context of this.

function renderCards() {
      return this.props.notes.map(note => {

The easy fix is to make it an arrow function

const renderCards = () => {
      return this.props.notes.map(note => {

There's lots of great explanations out there on why. Here's one that goes into great detail.

Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
1

Without any stack trace, this is where I see a potential issue:

render() {
    function renderCards() {
      // `this` is now your `renderCards` function and not your component, thus `this.props` doesn't work.
      return this.props.notes.map(note => {
        return (
            <NoteCard
                id={note.id}
                name={note.title}
            />
        );
      });
    }

    return (
        <div className="notes-container">{renderCards()}</div>
    )
  }

You can try moving renderCards() out of render():

renderCards() {
  return this.props.notes.map(note => (
    <NoteCard
      id={note.id}
      name={note.title}
    />
  ));
}

render() {
  return (
    <div className="notes-container">{this.renderCards()}</div>
  );
}
Jason Adkison
  • 151
  • 1
  • 4
  • That's not entirely accurate, the function is called in the current context therefor it doesn't need to be an arrow function or have the context bound. If it were being called in a different component/context then yes you have to juggle the context. – Jason Adkison Feb 12 '20 at 20:06
  • Thanks for retracting those comments. Have to remember that `this` depends on how a function gets called not where or how it was created. – Jason Adkison Feb 12 '20 at 20:32