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

Formik添加appleMusicId时意外重置timeDescription等字段值求助

Formik表单字段值重置问题排查与修复

问题描述

使用Formik结合本地song状态开发表单时,点击"Add Apple ID"按钮添加appleMusicId字段值后,timeDescription和sceneDescription字段值会被重置。首次提交时这两个字段有正常值,操作后再次提交,这两个字段值消失,仅保留其他字段内容。

相关代码

主组件代码

import { TextField } from "@/components/TextField";
import { Button } from "@chakra-ui/react";
import { Formik, Form } from "formik";
import { validate } from "graphql";
import { useState } from "react";

const initialValues = {
    name: "",
    artistName: "",
    episodeId: undefined,
    movieId: undefined,
    spotifyId: "",
    appleMusicId: "",
    sceneDescription: "",
    timeDescription: undefined,
};

const AddSong = () => {
    const [song, setSong] = useState(initialValues);

    const submit = (values: any) => {
        console.log(values, "values");
    }

    const addAppleId = () => {
        setSong((v) => {
            return {
                ...v,
                appleMusicId: "dummyId",
            };
        });
    };

    return (
        <Formik
            initialValues={song}
            onSubmit={(values) => submit(values)}
            validationSchema={validate}
            enableReinitialize={true}
        >
            <Form>
                <TextField name={"name"} label={"Name"} type={"text"} />
                <TextField name={"artistName"} label={"Artist Name"} type={"text"} />
                <TextField
                    name={"timeDescription"}
                    label={"Time Description"}
                    type={"number"}
                    placeholder="How many minutes in, was this song played?"
                />
                <TextField
                    name={"sceneDescription"}
                    label={"Scene Description"}
                    type={"text"}
                />
                <Button onClick={addAppleId}>Test Add Apple Id</Button>
                <Button isLoading={isLoading} type={"submit"}>
                    Create
                </Button>
            </Form>
        </Formik>
    )
};

TextField组件代码

export const TextField = ({ label, value, ...props }: TextFieldProps) => {
    const [field, meta] = useField(props);
    return (
        <FormControl
            isInvalid={
                meta.touched && (meta.error && meta.error?.length > 0 ? true : false)
            }
            mb={3}
        >
            <FormLabel
                aria-label={field.name}
                htmlFor={field.name}
                mb={0}
                textColor={useColorModeValue('gray.700', 'gray.100')}
            >
                {label}
            </FormLabel>
            <Input
                {...field}
                {...props}
                value={value ? value : undefined}
                autoComplete="off"
                borderRadius={0}
                paddingLeft={2}
                border={"2px solid"}
                borderColor={"gray.200"}
                _invalid={{ borderColor: "error-red" }}
            />
            <ErrorMessage
                name={field.name}
                component="span"
                className="text-sm pt-2 text-red-error"
            />
            <Text pt={1} fontSize={'sm'} color={useColorModeValue('gray.400', 'gray.600')}>{props.footerText}</Text>
        </FormControl>
    );
};

原因分析

  1. TextField组件手动覆盖value导致状态同步异常:在TextField的Input组件中,手动设置value={value ? value : undefined},打断了Formik的状态管理逻辑。当组件未从props传入value时(如timeDescription和sceneDescription字段),Input的value被强制设为undefined,而非Formik管理的当前字段值。结合enableReinitialize={true},当外部song状态更新时,Formik重新初始化表单,这两个字段的原有值被覆盖为undefined。
  2. 外部状态与Formik状态双管理冲突:通过本地song状态作为Formik的initialValues,同时用setSong修改字段值,再结合enableReinitialize,导致表单状态在外部状态和Formik内部状态之间反复同步,加剧了值丢失问题。

修复方案

方案1:修复TextField组件的value绑定

移除Input组件中手动设置的value属性,让Formik的field对象全权管理输入值,恢复Formik的状态同步能力:

export const TextField = ({ label, value, ...props }: TextFieldProps) => {
    const [field, meta] = useField(props);
    return (
        <FormControl
            isInvalid={
                meta.touched && (meta.error && meta.error?.length > 0 ? true : false)
            }
            mb={3}
        >
            <FormLabel
                aria-label={field.name}
                htmlFor={field.name}
                mb={0}
                textColor={useColorModeValue('gray.700', 'gray.100')}
            >
                {label}
            </FormLabel>
            <Input
                {...field}
                {...props}
                autoComplete="off"
                borderRadius={0}
                paddingLeft={2}
                border={"2px solid"}
                borderColor={"gray.200"}
                _invalid={{ borderColor: "error-red" }}
            />
            <ErrorMessage
                name={field.name}
                component="span"
                className="text-sm pt-2 text-red-error"
            />
            <Text pt={1} fontSize={'sm'} color={useColorModeValue('gray.400', 'gray.600')}>{props.footerText}</Text>
        </FormControl>
    );
};

方案2:移除外部状态,完全由Formik管理表单

如果不需要外部控制表单状态,可删除本地song状态,直接使用Formik的内部状态管理字段值,避免双状态冲突:

import { TextField } from "@/components/TextField";
import { Button } from "@chakra-ui/react";
import { Formik, Form, useFormikContext } from "formik";
import { validate } from "graphql";

const initialValues = {
    name: "",
    artistName: "",
    episodeId: undefined,
    movieId: undefined,
    spotifyId: "",
    appleMusicId: "",
    sceneDescription: "",
    timeDescription: undefined,
};

const AddSong = () => {
    const submit = (values: any) => {
        console.log(values, "values");
    };

    const AddAppleIdButton = () => {
        const { setFieldValue } = useFormikContext();
        const addAppleId = () => {
            setFieldValue('appleMusicId', 'dummyId');
        };
        return <Button onClick={addAppleId}>Test Add Apple Id</Button>;
    };

    return (
        <Formik
            initialValues={initialValues}
            onSubmit={(values) => submit(values)}
            validationSchema={validate}
        >
            <Form>
                <TextField name={"name"} label={"Name"} type={"text"} />
                <TextField name={"artistName"} label={"Artist Name"} type={"text"} />
                <TextField
                    name={"timeDescription"}
                    label={"Time Description"}
                    type={"number"}
                    placeholder="How many minutes in, was this song played?"
                />
                <TextField
                    name={"sceneDescription"}
                    label={"Scene Description"}
                    type={"text"}
                />
                <AddAppleIdButton />
                <Button isLoading={isLoading} type={"submit"}>
                    Create
                </Button>
            </Form>
        </Formik>
    )
};

说明

方案1适用于需要外部状态控制表单的场景,方案2更符合Formik的最佳实践,让Formik全权管理表单状态,避免状态同步问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:05:15