首页 > 解决方案 > 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

提前感谢您对此的帮助。

***** 更新 *****

  1. 我注意到我在客户端的 actions/weather.js 和 reducers/weather.js 以及服务器端的 routes/api/weather.js 中的控制台日志都没有触发。这告诉我这些模块一定不能执行。这可以解释为什么我在 client/src/components/pages/functional/Weather.js 中收到错误“无法读取未定义的属性‘当前’”。显然,我在这个链条中缺少一个环节。我只是看不出它是什么。

  2. 根据下面的输入,我尝试了一个小的重构。我试图查看是否存在某种命名冲突。这就是我在我的 React 功能组件中所做的:

// client/src/components/pages/functional/Weather.js

...

const mapStateToProps = state => ({weather: { forecast: state.forecast, loading: state.loading }});

...

它没有帮助。

标签: node.jsreactjsreduxreact-redux

解决方案


我看到你在这里的 combineReducers中设置为

export default combineReducers({
  alert,
  auth,
  weather

})

所以在商店里,东西被保存为{ alert: {...}, auth: {...}, weather: {...}}. 您可以尝试访问Weatheras中的预测值state.weather.forecast吗?

const mapStateToProps = state => ({ forecast: state.weather.forecast });

让我知道它是否有效。


推荐阅读