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

Formik表单提交后如何处理axios响应数据并在MUI卡片展示?

问题描述

我正在开发一个大学小型项目,需要创建Formik表单,输入水果名称后通过axios.get调用fruityvice API获取数据。要求在表单提交时执行请求,返回包含水果名称和营养信息的对象,供主页面(App.js)通过MUI卡片展示。但目前无法实现该功能,始终无法正确处理API返回的数据。

我的代码

import React, { useState } from 'react';
import { Formik, Form, Field, useFormik } from 'formik';
// import axios from 'axios';
import * as yup from "yup";
import { useTranslation } from 'react-i18next';
import { Button, Card, CardContent, TextField, Typography } from '@mui/material';

const API_URL = 'https://www.fruityvice.com/api';
const axios = require('axios');

function FormikMaterialUI({ fruits, totalWeight, setFruits, setTotalWeight }) {
    const { t } = useTranslation();

    const formik = useFormik({
        initialValues: {
            fruitName: '',
            fruitWeight: 0,
        },
        validationSchema: yup.object({
            fruitName: yup.string()
                .max(100, t('invalidName'))
                .required(t('requiredField')),
            fruitWeight: yup.number()
                .min(1, t('invalidWeight')),
        }),

        onSubmit: (values) => {
            const weight = values.fruitWeight;
            const fruit = values.fruitName;
            if (totalWeight + weight <= 400) {
                fetchFruitData(fruit)
                    .then((newFruit) => {
                        setFruits([...fruits, newFruit]);
                        setTotalWeight(totalWeight + weight);
                        formik.resetForm();
                    })
                    .catch((error) => {
                        console.error(error);
                    });
            }
        }
    });

    const fetchFruitData = async (fruit) => {
        try {
            const response = await axios.get(`${API_URL}/fruit/${fruit}`);
            const { name, nutritions } = response.data;
            const newFruit = { name, nutritions };
            return newFruit;
        } catch (error) {
            console.error(error);
            throw error;
        }
    };

    return (
        <div>
            <form onSubmit={formik.handleSubmit}>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(1, 2fr)", gridGap: "16px" }}>
                    <TextField
                        id="fruitName"
                        name="fruitName"
                        label={t('fruitName')}
                        type="text"
                        onChange={formik.handleChange}
                        onBlur={formik.handleBlur}
                        value={formik.values.fruitName}
                        error={formik.touched.fruitName && Boolean(formik.errors.fruitName)}
                        helperText={formik.touched.fruitName && formik.errors.fruitName}
                        style={{ margin: "8px 16px", backgroundColor: "peachpuff" }}
                    />
                    <TextField
                        id="fruitWeight"
                        name="fruitWeight"
                        label={t('weightGrams')}
                        type="number"
                        onChange={formik.handleChange}
                        onBlur={formik.handleBlur}
                        value={formik.values.fruitWeight}
                        error={formik.touched.fruitWeight && Boolean(formik.errors.fruitWeight)}
                        helperText={formik.touched.fruitWeight && formik.errors.fruitWeight}
                        style={{ margin: "8px 16px", backgroundColor: "peachpuff" }}
                    />
                </div>
                <div style={{ display: "flex", justifyContent: "center" }}>
                    <Button type="submit" variant="contained" style={{ margin: "0 auto", backgroundColor: "green" }}>
                        {t('addFruit')}
                    </Button>
                </div>
            </form>
            <p>{totalWeight}</p>
        </div>
    );
}

export default FormikMaterialUI;
解决方案

1. 修复API响应结构处理问题

fruityvice API的/fruit/{name}接口在匹配到多个相似水果时会返回数组而非单个对象,直接解构name和nutritions会导致报错。修改fetchFruitData函数适配两种返回格式:

const fetchFruitData = async (fruit) => {
    try {
        const response = await axios.get(`${API_URL}/fruit/${fruit}`);
        // 处理数组/对象两种返回格式,取第一个匹配项
        const fruitData = Array.isArray(response.data) ? response.data[0] : response.data;
        // 带上用户输入的重量,确保后续卡片能展示完整信息
        return { 
            name: fruitData.name, 
            nutritions: fruitData.nutritions,
            weight: formik.values.fruitWeight
        };
    } catch (error) {
        console.error('获取水果数据失败:', error);
        throw error;
    }
};

2. 解决状态更新的闭包陷阱

onSubmit中直接使用fruits和totalWeight会因闭包获取旧状态值,改用函数式更新确保拿到最新状态:

onSubmit: (values) => {
    const weight = Number(values.fruitWeight);
    const fruit = values.fruitName.trim();
    if (totalWeight + weight <= 400) {
        fetchFruitData(fruit)
            .then((newFruit) => {
                // 函数式更新获取最新状态
                setFruits(prevFruits => [...prevFruits, newFruit]);
                setTotalWeight(prevTotal => prevTotal + weight);
                formik.resetForm();
            })
            .catch((error) => {
                console.error(error);
            });
    }
}

3. 修正输入值类型转换

fruitWeight的输入值为字符串类型,需转为数字后再进行计算,避免出现字符串拼接导致的重量错误。

4. 优化导入方式

替换CommonJS风格的axios导入为ES6标准导入:

import axios from 'axios';

5. 添加用户友好的错误提示

在组件内增加错误状态管理,告知用户API请求失败的原因:

// 组件内新增状态
const [apiError, setApiError] = useState('');

// 修改fetchFruitData的catch逻辑
catch (error) {
    console.error('获取水果数据失败:', error);
    setApiError('无法获取该水果数据,请检查名称是否正确');
    throw error;
}

// 在return中添加错误提示组件
{apiError && (
    <Typography color="error" align="center" style={{ marginTop: 16 }}>
        {apiError}
    </Typography>
)}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:54:56