Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialDoug Hawkinson
Full Stack JavaScript Techdegree Student 25,073 PointsHaving trouble on FSJS Capstone project, moving data between front end and back end.
This is for my FSJS Developer Capstone project. I am working on a full stack app that utilizes Node.js on the server side and React/Redux on the client side. One of the functions of the app is to provide a current and eight-day weather forecast for the local area. The Weather route is selected from a menu selection on the client side that menu selection corresponds to a server side route that performs an axios.get that reaches out and consumes the weather api (in this case Darksky) and passes back that portion of the JSON api object pertaining to the current weather conditions and the eight-day weather forecast. There is more to the API JSON object but the app consume the "current" and "daily" segment of the total JSON object.
I have written a stand-alone version of the server-side axios "get" that successfully reaches out to the Darksky API and returns the data I am seeking. I am, therefore, reasonably confident my code will correctly bring back the data that I need. My problem consists in this: when I try to render the data in my React Component, the forecast object is undefined. That, of course, means there is nothing to render.
I have reviewed my code, read a plethora of documentation and even walked through tutorials that should help me find the problem and it still eludes me. So, I am stuck and would greatly appreciate some help. Most of the comment you still in the code below will be removed after the debugging process is completed.
I am including code blocks relevant to the problem:
My React Component
import React, { useEffect } from 'react';
import { connect, useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import Spinner from '../../helpers/Spinner'
import { getWeather } from '../../../redux/actions/weather'
const Weather = ({ getWeather, forecast, loading }) => {
const dispatch = useDispatch();
// upon load - execute useEffect() only once -- loads forecast into state
useEffect(() => { dispatch(getWeather()); }, [dispatch, getWeather])
return (
<div id='page-container'>
<div id='content-wrap' className='Weather'>
{ loading ?
<Spinner /> :
<>
<div className='WeatherHead box mt-3'>
<h4 className='report-head'>Weather Report</h4>
</div>
{/* Current Weather Conditions */}
<h6 className='current-head'>Current Conditions</h6>
<section className='CurrentlyGrid box mt-3'>
<span>Now</span>
<span>Summary</span>
<span>Icon</span>
<span>Precip</span>
<span>Temp</span>
<span>Humid</span>
<span>Visblty</span>
<span>Wind Spd</span>
<span><Moment parse='HH:mm'>`${forecast.currently.time}`</Moment></span>
<span>`${forecast.currently.summary}`</span>
<span>`${forecast.currently.icon}`</span>
<span>`${forecast.currently.precipProbability}`</span>
<span>`${forecast.currently.temperature}`</span>
<span>`${forecast.currently.humidity}`</span>
<span>`${forecast.currently.visibility}`</span>
<span>`${forecast.currently.windSpeed}`</span>
</section>
{/* Eight Day Forecast */}
{/* Code omitted for brevity */}
};
Weather.propTypes = {
getWeather: PropTypes.func.isRequired,
forecast: PropTypes.object.isRequired
};
const mapStateToProps = state => {
return { forecast: state.weather.forecast }
};
export default connect( mapStateToProps, { getWeather } )(Weather);
My React Action Creator
// node modules
import axios from 'axios';
import chalk from 'chalk';
// local modules
import {
GET_FORECAST,
FORECAST_ERROR
} from './types';
// Action Creator
export const getWeather = () => async dispatch => {
console.log(chalk.blue('ACTION CREATOR got here '));
try {
// get weather forecast
const res = await axios.get(`/api/weather`);
console.log(chalk.yellow('ACTION CREATOR getWeather ', res));
// pass only the currently & daily segments of the api
const forecast = {
currently: res.data.currently,
daily: res.data.daily.data
}
// SUCCESS - set the action -- type = GET_WEATHER & payload = res.data (the forecast)
dispatch({
type: GET_FORECAST,
payload: forecast
});
} catch (err) {
// FAIL - set the action FORECAST_ERROR, no payload to pass
console.log('FORECAST_ERROR ',err)
dispatch({
type: FORECAST_ERROR
});
};
};
My React Reducer
import {
GET_FORECAST,
FORECAST_ERROR,
} from '../actions/types'
const initialState = {
forecast: null,
loading: true
}
export default (state = initialState, action) => {
const { type, payload } = action
switch (type) {
case GET_FORECAST:
return {
...state,
forecast: payload,
loading: false
}
case FORECAST_ERROR:
return {
...state,
forecast: null,
loading: false
}
default:
return state
}
}
My Node Route
// node modules
const express = require('express');
const axios = require('axios');
const chalk = require('chalk');
const router = express.Router();
router.get('/weather', async (req, res) => {
try {
// build url to weather api
const keys = require('../../../client/src/config/keys');
const baseUrl = keys.darkskyBaseUrl;
const apiKey = keys.darkskyApiKey;
const lat = keys.locationLat;
const lng = keys.locationLng;
const url = `${baseUrl}${apiKey}/${lat},${lng}`;
console.log(chalk.blue('SERVER SIDE ROUTE FORECAST URL ', url));
const res = await axios.get(url);
// forecast -- strip down res, only using currently{} & daily{}
const weather = {
currently: res.data.currently,
daily: res.data.daily.data
};
console.log(chalk.yellow('SERVER SIDE ROUTE FORECAST DATA ', weather));
// return weather
res.json({ weather });
} catch (error) {
console.error(chalk.red('ERR ',error.message));
res.status(500).send('Server Error');
}
});
module.exports = router;
If the code snippets included are not sufficient, the repo resides here: https://github.com/dhawkinson/TH12-BnBConcierge
Thank you in advance for help.
1 Answer
Doug Hawkinson
Full Stack JavaScript Techdegree Student 25,073 Points@Blake Larson -- Two things:
I had the console.log to which you are referring in the position you suggest. It was not firing there either. I moved it to where it is now to try and grab 'res' before I set 'forecast'. It was a "fit of desperation" move that was no help.
The empty opening tag <> is closed by an empty closing tag below </>. It is a React shortcut for for inserting a <Fragment></Fragment> (check it out).
I have a stand-alone routine that I wrote as a proof of concept. It builds the url and performs the axios.get(url) and brilliantly console.logs the correct resulting object. So, I know I have formulated the approach correctly.
My problem seems to be that my routing is not working AT ALL. I have strategic console.log()s placed throughout the code. NONE of them are firing so that tells me the routines are not being traversed. That's where I am stuck. There is some crazy little thing that is preventing me from getting the JSON back and therefore the render is returning -- 'Cannot read property "currently" of null.' It's driving me nuts.
I appreciate that you took a look at it though.
Blake Larson
13,014 PointsBlake Larson
13,014 PointsIf you are console logging the forecast after the get request...
and get the proper result you can probably just use conditional rendering for the forecast like you did with loading.
{ loading || !forecast ? 'spinner' : 'content' }
There is also an empty opening tag on Weather.js line 27, but I think that would throw a syntax error and has nothing to do with the state being empty.