0

I have a presentational component, , which takes a {props} object and populates itself with the proper values.

I also have a container component, , which is supposed to map state to props, take the array of objects representing the plots, map it and initialize a from each one.

This is throwing me an error-Uncaught (in promise) TypeError: Cannot read property 'name' of undefined. The "name" in question is a property of the plot.

Looking at the documentation, this error typically happens when we import a component with curly braces when we should import it without, but that's not the case here.

I have a feeling that what is going on is that I am improperly passing the props to the element, but am not sure of the right way to do it.

Plot.js:

import React from 'react';

const Plot = ({ props }) => {
    return (
        <div className="media col-md-4">
            <div className="media-left">
                <a href="#">
                    <img className="media-object" src="http://placehold.it/200/550" alt="Placehold" />
                </a>
            </div>
            <div className="media-body">
                <h4 className="media-heading">{props.name}</h4>
                <ul>
                    <li><strong>Grower: </strong> {props.grower}</li>
                    <li><strong>Region: </strong> {props.region}</li>
                    <li><strong>Current State: </strong> {props.currentState}</li>
                    <li><strong>Next State: </strong> {props.nextState}</li>
                    <li><strong>Days To Next State: </strong> {props.daysToNext}</li>
                    <br />
                    <span class="pull-right">
                        <i id="like1" class="glyphicon glyphicon-thumbs-up"></i> <div id="like1-bs3"></div>
                        <i id="dislike1" class="glyphicon glyphicon-thumbs-down"></i> <div id="dislike1-bs3"></div>
                    </span>
                </ul>
            </div>
        </div>
    );
};

export default Plot;

Dashboard.js:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions';
import Plot from './plot';

class Dashboard extends Component {
    componentWillMount() {
        this.props.fetchMessage();
        this.props.fetchPlots();
    }

    render() {
        const plots = this.props.plots;
        return (
            <div>
                <span>{this.props.message}</span>
                <div className='container'>
                        <div className="row">
                            {plots && plots.map((plot) => <Plot key={plot._id} name={plot.name} grower={plot.grower} region={plot.region} currentState={plot.currentState} nextState={plot.nextState} daysToNext={plot.daysToNext}/>)}
                        </div>
                </div>
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        message: state.auth.message,
        plots: state.auth.plots
    }
}

export default connect(mapStateToProps, actions)(Dashboard);

EDIT:

This is fetchPlots(). I know that it's working and that plots are available as props, because when I add a console.(log) statement before the return statement in dashboard.js, I can see them, and they're fine, with all necessary properties.

export function fetchPlots() {
    return function (dispatch) {
        axios.get(`${ROOT_URL}/plots`, {
            headers: {
                authorization: localStorage.getItem('token')
            }
        })
            .then(response => {
                dispatch({
                    type: FETCH_PLOTS,
                    payload: response.data
                });
            });
    }
}

My reducer:

import {AUTH_USER, UNAUTH_USER, AUTH_ERROR, FETCH_MESSAGE, FETCH_PLOTS} from '../actions/types';

export default function(state={}, action){
    switch(action.type){
        case AUTH_USER:
            return{...state, error:'',authenticated:true};
        case UNAUTH_USER:
            return{...state, authenticated:false};
        case AUTH_ERROR:
            return { ...state, error: action.payload };
        case FETCH_MESSAGE:
            return { ...state, message: action.payload }
        case FETCH_PLOTS:
            return { ...state, plots: action.payload }
    }
    return state;
9
  • 1
    You should do your async in componentDidMount(). Please check and let us know if it helps. Btw can you paste your fetchMessage() and fetchPlots() snippets Commented Sep 27, 2017 at 7:44
  • @hawk you mean that I should call fetchMessage() and fetchPlots() in componentDidMount? I have checked, and the outputs of both are available in render(), they're properly being stored in state and retrieved from state with mapStateToProps. The message renders fine. Commented Sep 27, 2017 at 7:50
  • 1
    you only want to fetch your data once. componentDidMount gets called once. render gets called multiple times. Commented Sep 27, 2017 at 7:53
  • 1
    @BorisK Most likely problem is in action dispatching, can you share action handler with us Commented Sep 27, 2017 at 7:55
  • 1
    @BorisK yes a mean reducer Commented Sep 27, 2017 at 7:58

2 Answers 2

3

Change const Plot = ({ props }) into const Plot = (props)

Sign up to request clarification or add additional context in comments.

3 Comments

Nice, that did it! But why? What is the actual difference?
@BorisK You was trying to destruct props object and retrive props.props { props } means destructuring Destructuring assignment
@BorisK const Plot = ({ name }) this will also work
1

You are making an ajax call to get the data. So the ajax call takes some time to execute. Because of which this.props.plots would be undefined. So you need to make a null check in your Dashboard component

render() {
    const plots = this.props.plots;
    if(plots === undefined){
        return <div>Loading ...</div>
    }
    return (
        <div>
            <span>{this.props.message}</span>
            <div className='container'>
                    <div className="row">
                        {plots && plots.map((plot) => <Plot key={plot._id} name={plot.name} grower={plot.grower} region={plot.region} currentState={plot.currentState} nextState={plot.nextState} daysToNext={plot.daysToNext}/>)}
                    </div>
            </div>
        </div>
    );
}

Once the ajax call is finished the render function will be called again then the else condition will be followed and then it will not give the error you are getting.

Also make your api calls from componentDidMount() so that it's not called every time the state changes.

9 Comments

Uncaught (in promise) TypeError: Cannot read property 'name' of undefined
Any file name or line number it shows where it's getting error? @BorisK
@BorisK could you log props in Plot component and see what it prints?
Yes and now console log the props you are sending inside of your dashboard component and see what are you sending.
@ShubhamJain got it! Hawk was right-it was the way I was accessing props in the <plot /> component!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.