状态从Fetch操作返回未定义到Rails后端

时间:2019-09-04 15:43:37

标签: javascript ruby-on-rails reactjs redux react-redux

我已经设置了Rails后端(用于多种配料),并且以前可以运行,但是现在我的Dispatch Action Creator函数中的访存动作返回了undefined状态(不检索配料)。 API端点运行得很好(通过服务器检查),但是提取操作未检索到成分,并且返回了未定义的response.json

我到处都放置断点以检查状态。我也尝试过更改组件的mapStateToProps的内容,但是在进入undefined函数之前,状态为mapState

IngredientList组件

import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getIngredients, hideIngredients } from "../actions";

class IngredientList extends React.Component {
  render() {
    const { ingredients } = this.props;
    const ingredientsList = ingredients.map(ingredient => {
      return (
        <li key={ingredient.id}>
          {ingredient.id}. {ingredient.name}
        </li>
      );
    });

    return (
      <React.Fragment>
        <div>
          <button
            className="button"
            onClick={() => this.props.getIngredients()}
          >
            Get Ingredients
          </button>
          <button
            className="button"
            onClick={() => this.props.hideIngredients()}
          >
            Hide Ingredients
          </button>
        </div>

        <ul>{ingredientsList}</ul>
      </React.Fragment>
    );
  }
}

const structuredSelector = createStructuredSelector({
  ingredients: state => state.ingredients
});

const mapDispatchToProps = { getIngredients, hideIngredients };

export default connect(
  structuredSelector,
  mapDispatchToProps
)(IngredientList);

动作

export function getIngredientsRequest() {
  return {
    type: GET_INGREDIENTS_REQUEST
  };
}

export function getIngredientsSuccess(json) {
  return {
    type: GET_INGREDIENTS_SUCCESS,
    json
  };
}

export function hideIngredients() {
  return dispatch => {
    dispatch({ type: HIDE_INGREDIENTS });
  };
}

export function getIngredients() {
  return dispatch => {
    dispatch(getIngredientsRequest());
    return fetch(`v1/ingredients`)
      .then(response => response.json())
      .then(json => dispatch(getIngredientsSuccess(json)))
      .catch(error => console.log(error));
  };
}

减速器

const initialState = {
  ingredients: []
};

function rootReducer(state = initialState, action) {
  console.log(action.type);
  switch (action.type) {
    case "GET_INGREDIENTS_SUCCESS":
      console.log(action.json);
      return { ...state, ingredients: action.json.ingredients }
    case "GET_INGREDIENTS_REQUEST":
      console.log('Ingredients request received')
      return
    case "HIDE_INGREDIENTS":
      console.log('Ingredients are being hidden')
      return { ...state, ingredients: [] }
    case "GET_INGREDIENT_REQUEST":
      console.log('One Ingredient request received:', "id:", action.id)
      return
    case "GET_INGREDIENT_SUCCESS":
      console.log('GET_INGREDIENT_SUCCESS')
      const ingredients = action.json.ingredients;
      const id = action.id;

      return {
                ...state,
                ingredients: ingredients.filter(ingredient => ingredient.id.toString() === id)
            }
    default:
      return state
  }
}

export default rootReducer;

GET_INGREDIENTS_REQUEST reducers.js:6 
Ingredients request received reducers.js:12 

这在structuredSelector的{​​{1}}中

  

未捕获的TypeError:无法读取未定义的属性“成分”

1 个答案:

答案 0 :(得分:0)

我认为问题出在以下您试图在此处访问action.json.ingredients属性的减速器上:

switch (action.type) {
  case "GET_INGREDIENTS_SUCCESS":
    console.log(action.json);
    return { ...state, ingredients: action.json.ingredients } // here

在以下代码段中,请检查如何将响应值-变量json-传递给getIngredientsSuccess函数:

export function getIngredients() {
  return dispatch => {
    dispatch(getIngredientsRequest());
    return fetch(`v1/ingredients`)
      .then(response => response.json())
      .then(json => dispatch(getIngredientsSuccess(json))) // json value
      .catch(error => console.log(error));
  };
}

我的假设是,从API端点返回值的方式如下:

[
  {id: 12, name: 'test12'},
  {id: 13, name: 'test13'},
]

不是这样的:

{
  ingredients: [
    {id: 12, name: 'test12'},
    {id: 13, name: 'test13'},
  ]
}

因此,要解决此问题,您可能需要在reducer中更改以下行:

switch (action.type) {
  case "GET_INGREDIENTS_SUCCESS":
    console.log(action.json);
    // removed .ingredients
    // original value: action.json.ingredients 
    return { ...state, ingredients: action.json }

我希望这能解决您的问题,即使不仅仅是让我知道,我们也可以进行进一步调查。

另外,在渲染之前,需要在变量null上调用undefined函数之前,为变量处理mapingredients值,如下所示:

const { ingredients } = this.props;
const ingredientsList = ingredients != null ? ingredients.map(ingredient => {
  return (
    <li key={ingredient.id}>
      {ingredient.id}. {ingredient.name}
    </li>);
}) : null;

更新:

最初,您的代码调用getIngredientsRequest函数,该函数转到以下代码行,其中代码不返回状态对象:

case "GET_INGREDIENTS_REQUEST":
  console.log('Ingredients request received')
  return // missing state

所以我想下面的更正将在这里完成工作,很可能您不会再收到错误消息:

case "GET_INGREDIENTS_REQUEST":
  console.log('Ingredients request received')
  return state;

让我重点介绍关于reducer的return语句的重要一件事:

  

在Redux中,reducer采用起始状态和要处理的项目,然后返回新状态。

其他重要规则如下:

  

即使您没有更改状态,即使它只是null,也总是返回状态。您可能不会返回undefined。

因此,减速器应该在return语句中具有新状态,在这种情况下,它是undefined并导致错误消息。

请在此处进一步了解减速器:Actions and reducers: updating state