1

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!

Mike Holzbach
  • 45
  • 1
  • 2
  • 8
  • 1
    Why is the `state` defaulted to an array? Not sure if it is what causes the issue but might be worth checking out – Vinz243 Dec 31 '17 at 16:46

1 Answers1

2

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

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Hey Shubham, thank you for this. Your answer, and Vinz243's comment above have solved the specific problem I had here. It still isn't rendering the city name but I believe I will need to write a function to handle that since state here is more than just an array, so I can't simply .map() on it. I re-wrote my reducer as you said, but I also had to do WeatherReducer(state = initialState, action) for it to work. – Mike Holzbach Dec 31 '17 at 18:21