news 2026/9/19 20:33:38

Formik 2.x 迁移指南:从 v1 升级的破坏性变更、新特性与弃用警告全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Formik 2.x 迁移指南:从 v1 升级的破坏性变更、新特性与弃用警告全解析

Formik 2.x 迁移指南:从 v1 升级的破坏性变更、新特性与弃用警告全解析

【免费下载链接】formikBuild forms in React, without the tears 😭项目地址: https://gitcode.com/gh_mirrors/fo/formik

本指南以 Formik 仓库根目录的 MIGRATING-v2.md(与 packages/formik/MIGRATING-v2.md 内容一致)为骨架,系统梳理从 Formik 1.x 升级到 2.x 时必须注意的破坏性变更、2.x 引入的核心新特性(复选框/多选内建绑定、useField<Field as>、prop getters 等)以及官方弃用警告。读完本文,你将掌握resetFormsetStatusvalidatePromise 语义、TypeScript 泛型调整等迁移要点,并能直接套用文中的迁移后代码在 React 16.8+ 项目中落地。

适用前提:Formik 2 完全基于 React Hooks 构建,要求 React 16.8.x 及以上;同时由于 2.x 内部使用了unknown类型,使用 TypeScript 时需为 3.0 及以上版本。

一、破坏性变更(Breaking Changes)

Formik 2.x 引入的破坏性变更数量有限,官方明确表示"这些改动大概率不会影响太多人",但每一处都会在特定场景下改变既有行为,升级时必须逐一核对。

1. 最低环境要求

  • React 16.8.x 或更高:Formik 2 构建在 React Hooks 之上,低于该版本无法运行。
  • TypeScript 3.0 或更高(如果使用 TypeScript):因为 Formik 2 使用了unknown类型,旧版 TS 无法编译。

这一约束与 useFormik 完全基于React.useRefReact.useCallbackReact.useEffect等 Hooks 的实现直接相关,从源码结构看,整个表单状态机(stateRef+formikReducer)都依赖 Hooks 提供的能力。

2.resetForm:签名从"仅重置 values"变为"重置完整初始状态"

由于 2.x 新增了initialErrorsinitialTouchedinitialStatus三个 props,resetForm的签名随之改变——它现在接收的是Formik 的下一份初始状态(而不是仅仅下一个初始 values)。

v1 写法:

resetForm(nextValues);

v2 写法:

resetForm({ values: nextValues /* errors, touched, etc ... */ });

在源码中,resetForm的类型被定义在 types.tsx:

resetForm: (nextState?: Partial<FormikState<Values>>) => void;

其实现位于 Formik.tsx:当未传入nextState.values时回退到initialValues.currenterrorstouchedstatus也分别回退到initialErrors.currentinitialTouched.currentinitialStatus.current;若传入onReset回调,则先执行onReset(支持返回 Promise),再派发RESET_FORMaction 更新整个状态树(isSubmittingisValidatingsubmitCount也会按nextState或默认值重置)。因此:

  • 不带参数调用resetForm()会重置回全部初始状态;
  • { values: nextValues }调用则只覆盖 values,其余仍取初始值;
  • 你也可以传入errorstouchedstatusisSubmittingsubmitCount等任意字段做细粒度重置。

3.setError:请改用setStatus(status)

v1 中的setError在 2.x 中已移除,官方建议直接使用setStatus(status),两者行为完全一致。setStatus在 types.tsx 中定义,实现为向 reducer 派发SET_STATUSaction(见 Formik.tsx),用于手动设置表单顶层的status字段(例如提交失败时的服务端错误)。

4.validate:拒绝(reject)的 Promise 不再被当作校验错误

v1 中你可以从validate返回一个校验错误的 Promise,并且不区分 resolve 还是 reject——两种情况都会把 Promise 的载荷解释为校验错误对象。2.x 改变了这一语义:

  • reject 的 Promise 会被当作真实异常,不会更新表单的 errors 状态,只在非生产环境打印一条警告;
  • 所有返回"拒绝态错误 Promise"的校验函数,都必须改为返回 resolve 的错误对象 Promise

源码层面,Formik.tsx 的runValidateHandler明确定义了这一行为:Promise 走.then(errors => resolve(errors || emptyErrors)),而 rejection 分支reject(actualException)并输出Warning: An unhandled error was caught during validation in <Formik validate />。注意:这只会影响validate函数本身——通过validationSchema(Yup)产生的ValidationError会被yupToFormErrors捕获并正常转为错误对象(见 Formik.tsx),不受此变更影响。

5.ref:暂不支持直接挂到<Formik>

2.x 中仍无法通过refprop 直接获取 Formik 实例,官方提供innerRefprop 作为替代方案(React.forwardRef支持处于 WIP 状态)。<Formik>组件在 Formik.tsx 中通过React.useImperativeHandle(innerRef, () => formikbag)将完整的 formikbag 暴露给传入的innerRef,因此需要命令式调用表单方法时,应使用innerRef

6. TypeScript 类型变更

FormikActions更名为FormikHelpers

迁移非常简单,直接改名或通过 import alias 兼容旧代码:

v1:

import { FormikActions } from 'formik';

v2:

import { FormikHelpers as FormikActions } from 'formik';

在 types.tsx 中,FormikHelpers<Values>汇总了setStatussetErrorssetSubmittingsetTouchedsetValuessetFieldValuesetFieldErrorsetFieldTouchedvalidateFormvalidateFieldresetFormsubmitFormsetFormikState等全部命令式方法,它们会作为第二个参数传给onSubmit/onReset

FieldProps现在接受两个泛型参数

FieldProps的两个泛型参数都可选,但FormValues从第一个参数移动到了第二个参数

v1:

type Props = FieldProps<FormValues>;

v2:

type Props = FieldProps<FieldValue, FormValues>;

源码定义见 Field.tsx:

export interface FieldProps<V = any, FormValues = any> { field: FieldInputProps<V>; form: FormikProps<FormValues>; // if ppl want to restrict this for a given form, let them. meta: FieldMetaProps<V>; }

第一个泛型V对应字段值类型,第二个FormValues对应整个表单的 values 类型。如果你同时需要约束两者,务必按新顺序书写。

二、2.x 新增特性(What's New)

1. 复选框与多选(Checkboxes 和 Select multiple)的内建数组绑定

这是 Formik 1.x 中最令人困惑的点之一:2.x 像 Angular、Vue、Svelte 一样,"修复"了 React 复选框与多选下拉框,提供内建的数组绑定布尔值行为

其底层实现在 Formik.tsx 的getFieldProps中:

  • 普通复选框(无valueprop)field.checked = !!valueState,值存储为布尔值;
  • 同 name 的复选框组(带不同valuefield.checked = Array.isArray(valueState) && ~valueState.indexOf(valueProp),同时field.value = valueProp——Formik 自动把勾选的 value 绑定到同一个数组,增删逻辑全部替你完成;
  • radiofield.checked = valueState === valueProp
  • <select multiple>field.value = field.value || []并设置field.multiple = true

对应的写入侧逻辑位于executeChange(Formik.tsx)和getValueForCheckbox(Formik.tsx):当前值为布尔时返回Boolean(checked);当前值为数组时执行添加/移除;getSelectedValues(Formik.tsx)负责从多选下拉中提取选中值数组。

完整示例(来自迁移文档,可直接运行):

import React from 'react'; import { Formik, Field, Form } from 'formik'; import { Debug } from './Debug'; const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); const CheckboxExample = () => ( <div> <h1>Checkboxes</h1> <p> This example demonstrates how to properly create checkboxes with Formik. </p> <Formik initialValues={{ isAwesome: false, terms: false, newsletter: false, jobType: ['designer'], location: [], }} onSubmit={async values => { await sleep(1000); alert(JSON.stringify(values, null, 2)); }} > {({ isSubmitting, getFieldProps, handleChange, handleBlur, values }) => ( <Form> {/* This first checkbox will result in a boolean value being stored. */} <div className="label">Basic Info</div> <label> <Field type="checkbox" name="isAwesome" /> Are you awesome? </label> {/* Multiple checkboxes with the same name attribute, but different value attributes will be considered a "checkbox group". Formik will automagically bind the checked values to a single array for your benefit. All the add and remove logic will be taken care of for you. */} <div className="label"> What best describes you? (check all that apply) </div> <label> <Field type="checkbox" name="jobType" value="designer" /> Designer </label> <label> <Field type="checkbox" name="jobType" value="developer" /> Developer </label> <label> <Field type="checkbox" name="jobType" value="product" /> Product Manager </label> {/* You do not _need_ to use <Field>/useField to get this behavior, using handleChange, handleBlur, and values works as well. */} <label> <input type="checkbox" name="jobType" value="founder" checked={values.jobType.includes('founder')} onChange={handleChange} onBlur={handleBlur} /> CEO / Founder </label> {/* The <select> element will also behave the same way if you pass `multiple` prop to it. */} <label htmlFor="location">Where do you work?</label> <Field component="select" id="location" name="location" multiple={true} > <option value="NY">New York</option> <option value="SF">San Francisco</option> <option value="CH">Chicago</option> <option value="OTHER">Other</option> </Field> <label> <Field type="checkbox" name="terms" />I accept the terms and conditions. </label> {/* Here's how you can use a checkbox to show / hide another field */} {!!values.terms ? ( <div> <label> <Field type="checkbox" name="newsletter" /> Send me the newsletter <em style={{ color: 'rebeccapurple' }}> (This is only shown if terms = true) </em> </label> </div> ) : null} <button type="submit" disabled={isSubmitting}> Submit </button> <Debug /> </Form> )} </Formik> </div> ); export default CheckboxExample;

要点回顾:

  • 单个复选框(如isAwesometerms)直接绑定布尔值;
  • name不同value的复选框自动聚合成数组(jobType),勾选/取消自动增删;
  • 不使用<Field>/useField也可以:通过checked={values.jobType.includes('founder')}+handleChange/handleBlur获得相同行为;
  • <select multiple>与复选框组行为一致,选中值同样绑定为数组(location);
  • 复选框还能驱动条件渲染(terms为 true 时才显示newsletter)。

仓库中的 examples/checkboxes 与 examples/radio-group 目录提供了对应可运行示例。

2.useField():Hook 版的<Field>

useField就是"像<Field>一样的 Hook",详细用法见 docs/api/useField.md。其签名(Field.tsx):

export function useField<Val = any>( propsOrFieldName: string | FieldHookConfig<Val> ): [FieldInputProps<Val>, FieldMetaProps<Val>, FieldHelperProps<Val>]

返回一个三元组:字段输入 props(valuenamecheckedonChangeonBlurmultiple)、字段元数据(errortouchedinitialValueinitialTouchedinitialError)与命令式 helper(setValuesetTouchedsetError)。传入字符串时会被规范化为{ name }对象(Field.tsx),并自动完成字段注册/注销(Field.tsx)。

import React from 'react'; import { useField } from 'formik'; function MyTextField(props) { // 返回适用于 <input /> 的字段 props const [field, meta, helpers] = useField(props.name); return ( <> <input {...field} {...props} /> {meta.error && meta.touched && <div>{meta.error}</div>} </> ); }

FieldHookConfig<Field>props 的子集:name(必填)、validatetypemultiplevalue(仅 checkbox/radio 有效)。当配置对象中包含type: 'checkbox'type: 'radio'multiple: true等键时,useField返回的 props 会完全复刻<Field>的复选框/单选/多选行为。

3.useFormikContext():Hook 版的connect()

useFormikContext等价于 v1 的connect()HoC。它封装了React.useContext(FormikContext),并要求调用处必须是<Formik>的子组件(见 FormikContext.tsx):

export function useFormikContext<Values>() { const formik = React.useContext<FormikContextType<Values>>(FormikContext); invariant( !!formik, `Formik context is undefined, please verify you are calling useFormikContext() as child of a <Formik> component.` ); return formik; }

同时,FormikContextFormikProviderFormikConsumer均已从 FormikContext.tsx 导出,并经由 index.tsx 对外暴露,这让深层次组件无需层层透传 props 即可访问表单状态与 helpers。

4.<Field as>:直接向组件/字符串标签注入 props

<Field>新增asprop,它会直接把onChangeonBlurvalue等注入到目标组件或原生标签上。对使用 Emotion 或 styled-components 的开发者尤其友好——不再需要在包装函数里清理component的 render props。

// <input className="form-input" placeholder="Jane" /> <Field name="firstName" className="form-input" placeholder="Jane" /> // <textarea className="form-textarea"/> <Field name="message" as="textarea" className="form-textarea"/> // <select className="my-select"/> <Field name="colors" as="select" className="my-select"> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </Field> // with styled-components/emotion const MyStyledInput = styled.input` padding: .5em; border: 1px solid #eee; /* ... */ ` const MyStyledTextarea = MyStyledInput.withComponent('textarea'); // <input className="czx_123" placeholder="google.com" /> <Field name="website" as={MyStyledInput} placeholder="google.com"/> // <textarea placeholder="Post a message..." rows={5}></textarea> <Field name="message" as={MyStyledTextArea} placeholder="Post a message.." rows={4}/>

源码实现见 Field.tsx:as被解构为is(因为as在 TypeScript 中是保留字),渲染优先级为render→ 函数式childrencomponentasasElement = is || 'input',默认回退到<input>)。FieldConfigas的类型定义支持字符串标签或任意 React 组件(含ForwardRefExoticComponent,见 Field.tsx)。

5. Misc 杂项更新

  • FormikContext已导出(见上文);
  • validateOnMount?: boolean = false:新增 prop,默认关闭。开启后表单挂载完成即执行一次校验——源码中通过React.useEffectvalidateOnMount为 true 且isMounted时调用validateFormWithHighPriority(initialValues.current)(见 Formik.tsx);配合enableReinitialize时,initialValues 变化也会触发重新校验(Formik.tsx);
  • 新增initialErrorsinitialTouchedinitialStatusprops:分别用于指定表单初始错误、初始 touched、初始 status。它们通过React.useRef缓存(Formik.tsx),初始化时经cloneDeep进入 state(Formik.tsx),并作为FormikComputedProps只读暴露(types.tsx)。在enableReinitialize下,这些初始值变化会被同步回 state(Formik.tsx);
  • 顺带说明:isInitialValid已在开发模式下打印弃用警告,官方建议改用initialErrorsvalidateOnMount(Formik.tsx)。

6.getFieldProps(nameOrProps):prop getter 风格取字段输入

FormikProps新增getFieldPropsgetFieldMeta两个 prop getter(Kent C. Dodds 风格),适合偏好 prop drilling、未使用 context API,或需要自行构建自定义useField的场景。getFieldProps的返回类型定义于 types.tsx:

export interface FieldInputProps<Value> { /** Value of the field */ value: Value; /** Name of the field */ name: string; /** Multiple select? */ multiple?: boolean; /** Is the field checked? */ checked?: boolean; /** Change event handler */ onChange: FormikHandlers['handleChange']; /** Blur event handler */ onBlur: FormikHandlers['handleBlur']; }

调用方式:传入字符串(字段名)或配置对象均可;传入对象时才会触发 checkbox/radio/multiple select 的特化处理(见上文getFieldProps实现 Formik.tsx)。

7.getFieldMeta(name):取字段元数据

给定字段名,返回该字段的计算元数据,类型定义于 types.tsx:

export interface FieldMetaProps<Value> { /** Value of the field */ value: Value; /** Error message of the field */ error?: string; /** Has the field been visited? */ touched: boolean; /** Initial value of the field */ initialValue?: Value; /** Initial touched state of the field */ initialTouched: boolean; /** Initial error message of the field */ initialError?: string; }

实现见 Formik.tsx:value/error/touched分别从state.values/state.errors/state.touched中按路径取值(支持a.b.c这类嵌套路径,配合getIn),initialValue/initialTouched/initialError则取自三个 initial ref。这与useField返回的第二个元素完全一致,因此getFieldMeta也是自定义 Hook 或组件内部取元数据(如展示校验状态、样式切换)的常用入口。

三、弃用警告(Deprecation Warnings)

所有renderprops 均已弃用(控制台会打印警告)

对于<Field><FastField><Formik><FieldArray>renderprop 已被弃用并伴随控制台警告,将在未来版本中移除,官方建议改用child callback function(子函数),这一设计是为了与 React Context Consumer 的用法对齐。

- <Field name="firstName" render={props => ....} /> + <Field name="firstName">{props => ... }</Field>

源码中的对应警告可见于多处:

  • <Field render>的弃用警告与提示文案在 Field.tsx;
  • <Formik render>的弃用警告在 Formik.tsx;
  • FormikConfig.renderSharedRenderProps.render字段也均在类型定义上标注了@deprecated(types.tsx、types.tsx)。

注意<Field>的渲染优先级:render存在时仍会执行(向后兼容),随后依次是函数式childrencomponentas;同时源码会在开发环境对"as/component/render与函数式children混用"给出 invariant 警告(Field.tsx),迁移时应避免同时使用多种渲染方式。

四、迁移检查清单

升级到 Formik 2.x 时,建议按以下顺序逐项核对(均可在本仓库 packages/formik 目录下找到源码佐证):

  1. 确认 React ≥ 16.8、TypeScript ≥ 3.0;
  2. 全局搜索resetForm(,将单参数调用改为resetForm({ values })或空参数;
  3. 全局搜索setError,改用setStatus
  4. 检查validate函数是否返回 rejected Promise,一律改为 resolve;
  5. 若通过ref访问 Formik 实例,改用innerRef
  6. FormikActions改为FormikHelpers,并按FieldProps<FieldValue, FormValues>的新顺序书写泛型;
  7. 移除或替换所有renderprop(控制台警告会逐个提示),统一使用子函数写法;
  8. 可选:将 checkbox/多选逻辑简化——利用 2.x 的内建数组/布尔绑定替换手写的checked/onChange胶水代码;
  9. 可选:用useFielduseFormikContext<Field as>getFieldProps/getFieldMeta重构自定义输入组件,减少重复的 props 透传。

对于更详尽的 2.x API 说明,可继续阅读仓库中的 docs/api 文档(如 useFormik.md、Field.md、useField.md)与 packages/formik/CHANGELOG.md;若你还在 1.x 上并想了解 1.x 与 2.x 的行为差异,也可参考根目录 README.md 中关于 v2 的说明。

【免费下载链接】formikBuild forms in React, without the tears 😭项目地址: https://gitcode.com/gh_mirrors/fo/formik

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 20:32:10

URP半透明渲染深度排序优化:Shader与Renderer Feature实战

1. 半透明渲染的痛点与URP管线特性拆解做过Unity项目的人大概率都遇到过这种场景&#xff1a;一个玻璃杯、一片树叶、一团烟雾&#xff0c;或者角色身上半透明的披风&#xff0c;在镜头转动到某些角度时&#xff0c;突然出现奇怪的色块、闪烁的条纹&#xff0c;或者前后层叠关系…

作者头像 李华
网站建设 2026/9/19 20:31:43

FUI验证实战:从Prefab节点改名到构建门禁自动化诊断

FUI 验证实战&#xff1a;从 Prefab 节点改名到生成诊断与构建门禁见过太多次这种场景了&#xff1a;某个周二的下午&#xff0c;策划在走查界面时顺口提了一句"这个按钮名字太随意了&#xff0c;改成BagButton吧"&#xff0c;程序随手在编辑器里把 Prefab 的节点重命…

作者头像 李华
网站建设 2026/9/19 20:29:32

大模型学习宝典:从Transformer到高效微调实战

1. 项目概述"大模型学习宝典"是一套面向AI从业者和深度学习爱好者的系统性学习指南&#xff0c;重点覆盖从Transformer基础架构到高效微调技术的完整知识体系。这个手册的独特价值在于&#xff1a;它不像传统教材那样按部就班讲解理论&#xff0c;而是以工业级应用为…

作者头像 李华