Search code examples
reactjsreact-reduxflux

this.props.myActionCreator throws 'TypeError: Cannot read property 'props' of null'


Just to mention, I did not find an answers for my particular situation on similar post.

My app allows a user to get a city's weather along with a relevant packing list for that weather.

I want to make each city's card deletable and have an 'X' placed that should call an updateTerms action creator, which would in turn remove it from the users list and update the dom.

The callback seems to fire, but this.props.updateTerms throws **TypeError: Cannot read property 'props' of null at handleDeleteCard **

a_result_card.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import ATodoList from './a_Todo_List';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import { bindActionCreators} from 'redux';
import { updateTerms } from '../actions';

class AResultCard extends Component {
    constructor(props) {
        super(props); 
        this.state = { cityName : '' }; 
        this.handleDeleteCard = this.handleDeleteCard.bind(this);
    }

    handleDeleteCard(event) { // I NEED TO CALL THIS WITH THE KEY
        //So THAT I CAN REMOVE IT FROM THE USERS TERM LIST
        event.preventDefault();
        debugger;
        this.props.updateTerms(cityName, "delete");
    }

    renderWeather(cityData) {
        function calcAverageTemp(days) {
            let reduced = days.reduce((sum,day) => {
               return sum + day.main.temp_max;
            }, 0);
            return Math.floor(reduced / cityData.list.length);
        };
        function calcAverageHumidity(days) {
            let reduced = days.reduce((sum,day) => {
               return sum + day.main.humidity;
            }, 0);
            return Math.floor(reduced / cityData.list.length);
        };
        const avgTempK = calcAverageTemp(cityData.list);
        const cityName = cityData.city.name;
        const avgTempF = Math.floor((avgTempK * 1.8) - 459.67 );
        const avgHumidity = calcAverageHumidity(cityData.list);

        return (
            <li key={cityName} className="result-card">
                <div className="card">
                    <div className="">
                        <div className="weather-details">
                            <div className="card-heading">
                                <span className="weather-detail detail-title">{cityName}</span>
                                <span className="weather-detail">{avgTempF} &#176;F</span>
                                <span className="weather-detail">H: {avgHumidity}%</span>
                                <span className="weather-detail delete-card-icon" onClick={handleDeleteCard}>X</span>
                            </div>
                        </div>
                        <ATodoList avgTempF={avgTempF} />
                    </div>
                </div>
            </li>
        );
    }


    render() {
        return (
            <ul className="results">
                <CSSTransitionGroup
                    transitionName="example"
                    transitionEnterTimeout={150}
                    transitionLeaveTimeout={150}>

                {this.props.weather.map(this.renderWeather)}
                </CSSTransitionGroup>
            </ul>
        );
    }
}

function mapStateToProps({weather, userTerms}) {
    return ({
            weather,
            userTerms
        });
}
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ updateTerms }, dispatch);
}

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

Solution

  • You need to call your handleDeleteCard function like

    <span className="weather-detail delete-card-icon" onClick={this.handleDeleteCard}>X</span>
    

    since it belongs to the context of the React Component and not the renderWeather function

    Also bind the renderWeather function

    renderWeather = (cityData) => {
        function calcAverageTemp(days) {
            let reduced = days.reduce((sum,day) => {
               return sum + day.main.temp_max;
            }, 0);
            return Math.floor(reduced / cityData.list.length);
        };
        function calcAverageHumidity(days) {
            let reduced = days.reduce((sum,day) => {
               return sum + day.main.humidity;
            }, 0);
            return Math.floor(reduced / cityData.list.length);
        };
        const avgTempK = calcAverageTemp(cityData.list);
        const cityName = cityData.city.name;
        const avgTempF = Math.floor((avgTempK * 1.8) - 459.67 );
        const avgHumidity = calcAverageHumidity(cityData.list);
    
        return (
            <li key={cityName} className="result-card">
                <div className="card">
                    <div className="">
                        <div className="weather-details">
                            <div className="card-heading">
                                <span className="weather-detail detail-title">{cityName}</span>
                                <span className="weather-detail">{avgTempF} &#176;F</span>
                                <span className="weather-detail">H: {avgHumidity}%</span>
                                <span className="weather-detail delete-card-icon" onClick={this.handleDeleteCard}>X</span>
                            </div>
                        </div>
                        <ATodoList avgTempF={avgTempF} />
                    </div>
                </div>
            </li>
        );
    }