node.js - Node/React/Redux:在 Node 和 React 之间传递 api JSON 对象时遇到问题
问题描述
我是使用 Node 的 React/redux 新手。我正在开发一个全栈应用程序,该应用程序在服务器端使用 Node.js,在客户端使用 React/Redux。该应用程序的功能之一是为当地提供当前和八天的天气预报。Weather 路由是从客户端的菜单选择中选择的,该菜单选择对应于执行 axios.get 的服务器端路由,该路由伸出并使用天气 api(在本例中为 Darksky)并传回 JSON 的那部分与当前天气状况和八天天气预报有关的 api 对象。API JSON 对象还有更多内容,但应用程序使用整个 JSON 对象的“当前”和“每日”部分。
我已经编写了服务器端 axios“get”的独立版本,它成功地连接到 Darksky API 并返回我正在寻找的数据。因此,我有理由相信我的代码将正确地带回我需要的数据。我的问题在于:当我尝试在我的 React 组件中渲染数据时,预测对象是未定义的。当然,这意味着没有要渲染的内容。
我已经查看了我的代码,阅读了大量的文档,甚至浏览了可以帮助我找到问题的教程,但它仍然让我难以理解。所以,我被困住了,非常感谢一些帮助。调试过程完成后,您仍然在下面代码中的大部分注释将被删除。
我包括与问题相关的代码块:
我的反应组件
// client/src/components/pages/functional/Weather.js
import React, { useEffect } from 'react';
import { connect } 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, weather: { forecast, loading } }) => {
// upon load - execute useEffect() only once -- loads forecast into state
useEffect(() => { getWeather(); }, [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'>
/* additional rendering code removed for brevity */
<span><Moment parse='HH:mm'>`${forecast.currently.time}`</Moment></span>
/* additional rendering code removed for brevity */
</section>
</>
}
</div>
</div>
);
};
Weather.propTypes = {
getWeather: PropTypes.func.isRequired,
weather: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ forecast: state.forecast });
export default connect( mapStateToProps, { getWeather } )(Weather);
我的反应动作创建者
// client/src/redux/actions/weather.js
import axios from 'axios';
import chalk from 'chalk';
// local modules
import {
GET_FORECAST,
FORECAST_ERROR
} from './types';
// Action Creator
export const getWeather = () => async dispatch => {
try {
// get weather forecast
const res = await axios.get(`/api/weather`);
console.log(chalk.yellow('ACTION CREATOR getWeather ', res));
// SUCCESS - set the action -- type = GET_WEATHER & payload = res.data (the forecast)
dispatch({
type: GET_FORECAST,
payload: res.data
});
} catch (err) {
// FAIL - set the action FORECAST_ERROR, no payload to pass
console.log('FORECAST_ERROR ',err)
dispatch({
type: FORECAST_ERROR
});
};
};
我的 React 减速器
// client/src/redux/reducers/weather.js
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
}
}
我的节点路由
// server/routes/api/weather.js
const express = require('express');
const axios = require('axios');
const chalk = require('chalk');
const router = express.Router();
// ***** route: GET to /api/weather
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;
我的 Express 服务器中间件与路由有关(只是为了彻底)
// server/index.js
/* code deleted for brevity */
// define routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/weather', require('./routes/api/weather'));
app.use('/api/favorites', require('./routes/api/favorites'));
/* code deleted for brevity */
如果包含的代码片段不够用,repo 位于此处:https ://github.com/dhawkinson/TH12-BnBConcierge
提前感谢您对此的帮助。
***** 更新 *****
我注意到我在客户端的 actions/weather.js 和 reducers/weather.js 以及服务器端的 routes/api/weather.js 中的控制台日志都没有触发。这告诉我这些模块一定不能执行。这可以解释为什么我在 client/src/components/pages/functional/Weather.js 中收到错误“无法读取未定义的属性‘当前’”。显然,我在这个链条中缺少一个环节。我只是看不出它是什么。
根据下面的输入,我尝试了一个小的重构。我试图查看是否存在某种命名冲突。这就是我在我的 React 功能组件中所做的:
// client/src/components/pages/functional/Weather.js
...
const mapStateToProps = state => ({weather: { forecast: state.forecast, loading: state.loading }});
...
它没有帮助。
解决方案
我看到你在这里的 combineReducers中设置为
export default combineReducers({
alert,
auth,
weather
})
所以在商店里,东西被保存为{ alert: {...}, auth: {...}, weather: {...}}
. 您可以尝试访问Weather
as中的预测值state.weather.forecast
吗?
const mapStateToProps = state => ({ forecast: state.weather.forecast });
让我知道它是否有效。
推荐阅读
- android - 以编程方式区分我的自定义呼叫应用程序或默认 android 手机应用程序之间的来电
- python - python在不改变舍入的情况下抑制科学记数法
- javascript - 如何使用猫鼬虚拟将文档的内容显示到另一个文档中
- javascript - 在 Angualr + Electron 应用程序中从用户那里获取目录输入
- python - Is there a (simple) way to calculate the percentage (physical) space occupied by an ad in a webpage using Python?
- python-3.x - how to convert python pyinstaller distribution directory to Debian package
- python - 如何以自定义格式在 JSON 文件中编写包含对的 python 列表?
- flutter - 返回类型“小部件?” 不是闭包上下文所要求的“小部件”
- unit-testing - 如何测试 Box 中错误的类型
? - python - loop multiple lists if exist and check for match (Python)