You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React添加新食谱功能报错求助:Axios POST请求失败排查

问题分析与解决方案

我来帮你梳理下代码里的问题,一步步搞定这个添加食谱的功能:

1. Axios POST 请求的核心错误

你现在的axios.post只传了URL,没有携带请求体,后端肯定接收不到数据;而且你先更新了本地的recipes状态再发请求——万一请求失败,本地状态就和后端数据不一致了,逻辑完全反了。

正确的思路是:先把表单数据传给后端,请求成功后再更新本地状态,还要加上错误处理方便排查问题。

2. Context 与 Reducer 的混乱使用

你同时用了useState和useReducer,状态管理逻辑冲突了;而且RecipeProvider里的value写错了(addTransaction应该是addRecipe),Reducer里的state结构也不对——你的初始state是数组,但Reducer里却用...state, recipes: [...],这会把数组变成对象,导致后续读取数据出错。

3. 表单提交的小坑

  • 表单里的+ Přidat按钮默认是submit类型,会触发表单提交刷新页面,得改成type="button"
  • 你定义了score状态,但表单里没有对应的输入框,提交的时候score一直是0
  • 表单的提交应该绑定在<form>的onSubmit上,而不是单独的+按钮,不然表单的默认行为没处理好

修正后的代码

1. Recipe.js(Form组件)

import React, { useContext, useState } from 'react';
import './Form.css';
import { RecipeContext } from './RecipeContext';
import axios from 'axios';

function Form() {
  const [nazev, setNazev] = useState('');
  const [uvodni, setUvodni] = useState('');
  const [ingredience, setIngredience] = useState('');
  const [postup, setPostup] = useState('');
  const [time, setTime] = useState(0);
  const [score, setScore] = useState(0);
  const { addRecipe } = useContext(RecipeContext); // 改用Reducer的addRecipe方法

  const onSubmit = async (e) => {
    e.preventDefault();
    // 先校验必填字段(可选,但能避免无效请求)
    if (!nazev || !postup) {
      alert('请填写食谱名称和步骤!');
      return;
    }

    try {
      // 构造后端需要的请求体(要和API文档字段对应!)
      const newRecipeData = {
        name: nazev,
        info: uvodni,
        description: postup,
        duration: Number(time),
        score: Number(score),
        // 假设食材用逗号分隔,转成数组传给后端
        ingredients: ingredience.split(',').map(item => item.trim())
      };

      // 发送POST请求,等待后端响应
      const res = await axios.post("https://cookbook.ack.ee/api/v1/recipes", newRecipeData);
      // 请求成功后,用后端返回的真实数据更新本地状态(比自己生成ID更可靠)
      addRecipe(res.data);

      // 清空表单
      setNazev('');
      setUvodni('');
      setIngredience('');
      setPostup('');
      setTime(0);
      setScore(0);
    } catch (error) {
      // 错误处理,打印详细信息方便排查
      console.error('添加食谱失败:', error.response?.data || error.message);
      alert('添加食谱失败,请稍后重试');
    }
  };

  return (
    <>
      <form className="form" onSubmit={onSubmit}> {/* 把提交事件绑在form上 */}
        <p className="nazev">Název receptu</p>
        <input type="text" value={nazev} onChange={(e) => setNazev(e.target.value)} required />
        <input 
          type="text" 
          className="uvodni" 
          placeholder="Úvodní text" 
          value={uvodni} 
          onChange={(e) => setUvodni(e.target.value)} 
        />
        <h2>Ingredience</h2>
        <input 
          placeholder="Vaše ingredience (用逗号分隔)" 
          type="text" 
          value={ingredience} 
          onChange={(e) => setIngredience(e.target.value)} 
        />
        {/* 改成button类型,避免触发表单提交 */}
        <button type="button">+ Přidat</button>
        <input 
          type="text" 
          className="postup" 
          placeholder="Postup" 
          value={postup} 
          onChange={(e) => setPostup(e.target.value)} 
          required 
        />
        <input 
          type="number" 
          placeholder="Čas" 
          className="cas" 
          value={time} 
          onChange={(e) => setTime(e.target.value)} 
        />
        {/* 添加score输入框 */}
        <input 
          type="number" 
          placeholder="Hodnocení" 
          value={score} 
          onChange={(e) => setScore(e.target.value)} 
          min="0" 
          max="5" 
        />
        {/* 提交按钮 */}
        <button type="submit" className="pridat">+ Přidat recept</button>
      </form>
    </>
  );
}

export default Form;

2. RecipeContext.js

import React, { useEffect, createContext, useReducer } from 'react';
import axios from 'axios';
import AppReducer from './AppReducer';

export const RecipeContext = createContext();

export default function RecipeProvider(props) {
  // 初始状态定义为包含recipes数组的对象,和Reducer结构对应
  const initialState = {
    recipes: []
  };

  const [state, dispatch] = useReducer(AppReducer, initialState);

  // 添加食谱的方法,传给组件使用
  function addRecipe(recipe) {
    dispatch({
      type: 'ADD_RECIPE',
      payload: recipe
    });
  }

  useEffect(() => {
    // 获取初始食谱列表
    const fetchRecipes = async () => {
      try {
        const res = await axios.get('https://cookbook.ack.ee/api/v1/recipes?limit=10&offset=0');
        // 用Reducer初始化recipes状态
        dispatch({
          type: 'SET_RECIPES',
          payload: res.data
        });
      } catch (error) {
        console.error('获取食谱列表失败:', error);
      }
    };

    fetchRecipes();
  }, []); // 空依赖数组,只执行一次

  return (
    <RecipeContext.Provider value={{ 
      recipes: state.recipes, 
      addRecipe 
    }}>
      {props.children}
    </RecipeContext.Provider>
  );
}

3. AppReducer.js

export default (state, action) => {
  switch(action.type) {
    case 'SET_RECIPES':
      return {
        ...state,
        recipes: action.payload
      };
    case 'ADD_RECIPE':
      return {
        ...state,
        // 把新食谱加到数组前面,也可以放后面,看你需求
        recipes: [action.payload, ...state.recipes]
      };
    default:
      return state;
  }
};

关键说明

  • Axios POST:一定要传请求体,用async/await处理异步逻辑,确保请求成功后再更新本地状态,避免数据不一致。
  • 状态管理:统一用useReducer管理状态,避免useState和useReducer混用导致的混乱;初始状态要和Reducer结构一致。
  • 表单处理:把提交事件绑在<form>上,用e.preventDefault()阻止默认刷新,按钮类型要正确,避免意外提交。

内容的提问来源于stack exchange,提问作者jendazedniik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:06:55