Search code examples
javascriptreactjsreact-reduxredux-promise

Accessing elements of reducer payload that is an object


This is a basic weather app that I'm doing to learn Redux. The API does not provide the city name that one searches for, so I must pass it via Redux.

I have the following container:

import React, { Component } from "react";
import { connect } from "react-redux";

class WeatherList extends Component {
  renderWeather = cityData => {
    const conditions =
      cityData.forecast.simpleforecast.forecastday[0].conditions;
    const fHigh =
      cityData.forecast.simpleforecast.forecastday[0].high.fahrenheit;
    return (
      <tr>
        {/* <td>{cityData.city}</td> */}
        <td>{cityData.meta.city}</td>
        <td>{conditions}</td>
        <td>{fHigh}</td>
      </tr>
    );
  };
  render() {
    return (
      <table className="table table-hover">
        <thead>
          <tr>
            <th>City</th>
            <th>Conditions</th>
            <th>High (F)</th>
            <th>Humidity</th>
          </tr>
        </thead>
        {/* <tbody>{this.props.weather.map(this.renderWeather)}</tbody> */}
        <tbody>{this.props.weather.data.map(this.renderWeather)}</tbody>
      </table>
    );
  }
}

const mapStateToProps = ({ weather }) => ({
  weather
});

export default connect(mapStateToProps)(WeatherList);

this.props.weather.data.map is throwing an error of "cannot read property map of undefined".

The reducer that is providing the "weather" state is:

import { FETCH_WEATHER } from "../actions/index";

export function WeatherReducer(state = [], action) {
  switch (action.type) {
    case FETCH_WEATHER:
      console.log(action.payload.data);
      console.log(action.meta.city);
      return { data: [action.payload.data, ...state], meta: action.meta.city };
    // return [action.payload.data, ...state];
  }
  return state;
}

And finally here is the relevant action creator:

import axios from "axios";

const API_KEY = "e95fb12f6c69ae61";
const ROOT_URL = `http://api.wunderground.com/api/${API_KEY}/forecast/q/`;

export const FETCH_WEATHER = "FETCH_WEATHER";

export function fetchWeather(searchData) {
  const url = `${ROOT_URL}${searchData.stateName}/${searchData.city}.json`;
  const request = axios.get(url);

  return {
    type: FETCH_WEATHER,
    payload: request,
    meta: { city: searchData.city }
  };
}

You can see from the commented out code that I can get this to work if I only pass along an array to iterate over. But I need to pass more than that in order to get the city name that a person searches for. What can I do to read that first element of the state object, the array, and get rid of the undefined error?

Many thanks for any ideas!


Solution

  • Since WeatherReducer returns an object with data and meta properties, you must declare it as an object in initialState. Your reducer must look like

    const initialState = {
        data: [],
        meta: ''
    }
    export function WeatherReducer(state = initialState, action) {
      switch (action.type) {
        case FETCH_WEATHER:
          console.log(action.payload.data);
          console.log(action.meta.city);
          return { data: [action.payload.data, ...state.data], meta: action.meta.city };
      }
      return state;
    }
    

    The error might come because initially an empty array is returned as the reducer value before fetchWeather action is triggered and thus this.props.weather.data would be undefined. Another thing to follow in such cases is to conditionally use all of such values which can be undefined at certain point of time