多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Formik 2.x 版本演进全解读:React 19 兼容、类型安全与校验性能优化的完整变更图谱

Formik 2.x 版本演进全解读:React 19 兼容、类型安全与校验性能优化的完整变更图谱 Formik 2.x 版本演进全解读React 19 兼容、类型安全与校验性能优化的完整变更图谱【免费下载链接】formikBuild forms in React, without the tears 项目地址: https://gitcode.com/gh_mirrors/fo/formik导读packages/formik/CHANGELOG.md 完整记录了 Formik 2.x 系列从 2.1.7 到 2.4.9 共 28 个版本的迭代历程。本文以这份变更日志为骨架系统梳理 Formik 在 React 19 兼容、TypeScript 类型体系、Yup 校验集成、FieldArray 性能优化与函数式状态更新等维度的演进脉络并结合 packages/formik/src 下的源码实现与测试用例帮助你理解每个变更背后的动机、对现有项目的实际影响以及升级时应当关注的迁移要点。一、版本演进总览2.x 系列的时间线与发布节奏从 2.1.7 到 2.4.9Formik 的版本号遵循 changesets 语义化发布规范README 中明确说明该项目使用 changesets 管理发布说明每次发布分为两类变更变更类型含义代表性版本Minor Changes向后兼容的新功能如新增 API 能力、类型泛型支持2.2.0、2.3.0、2.4.0Patch Changes缺陷修复、类型修正、依赖升级、性能微调其余全部版本值得注意的是2.x 系列在功能上始终保持高度稳定绝大多数版本是 Patch Changes仅在三个版本引入了 Minor 功能setValues函数式更新2.2.0、ArrayHelpers泛型与 Yup 跨字段校验2.3.0、Yup transforms 支持2.4.0。这种发布策略意味着对于大多数使用者2.x 内部的小版本升级风险很低可以放心跟随。当前仓库中 packages/formik/package.json 的version字段为2.4.9即这份 CHANGELOG 记录的最终版本可作为判定项目当前处于 2.x 序列末端的依据。二、React 19 兼容性适配2.x 系列收尾阶段的核心工程CHANGELOG 中最新两个版本2.4.8、2.4.9的全部工作都聚焦于 React 19 兼容这是 2.x 系列后期最重要的技术主题。2.1 替换全局 JSX 命名空间2.4.8React 19 移除了全局JSX命名空间导致使用了JSX.IntrinsicElements的代码在严格类型检查下编译失败。2.4.8 将内部类型引用从JSX.IntrinsicElements替换为React.JSX.IntrinsicElements这是一个纯类型层面的修改不改变任何运行时行为但对所有使用 TypeScript 且升级到 React 19 的项目是必需的修复——否则Field、Form等组件的 props 类型将无法通过编译。2.2 修复 React 19 的 JSX ref 传递2.4.92.4.9 修复了 React 19 下jsx ref的问题PR 编号 4051由社区贡献者 Moumouls 提交。React 19 对 ref 的处理机制发生变化ref 可作为普通 prop 传递Formik 通过内部 ref 转发机制基于hoist-non-react-statics见 packages/formik/package.json 的依赖清单向下传递 ref 时需要进行相应调整。对升级者的建议如果你计划升级到 React 19请确保 Formik 版本不低于 2.4.9如果使用 TypeScript2.4.8 的React.JSX修复同样不可或缺。这两个版本应视为React 19 就绪的最低门槛。三、TypeScript 类型体系演进从类型修复到泛型增强2.x 系列的 Patch 版本中有相当比例是类型层面的打磨这些变更虽然不改变运行时行为但对 TS 用户的实际开发体验影响巨大。3.1 类型修复类变更一览版本变更内容涉及文件2.4.5补充缺失依赖types/hoist-non-react-statics修复类型安装不完整的问题issue #3837packages/formik/package.json2.4.4移除已废弃的StatelessComponent类型改用FunctionComponentsrc 下各组件2.4.3修复FormikHelper与FieldHelperProps类型packages/formik/src/types.tsx2.4.2内部类型全面适配 React 18—2.4.1修正setFieldValue的函数签名类型—2.2.8修正setError的取值类型使其与setFieldError的 message 类型一致—2.1.7全量替换废弃的React.SFC为React.FC—这些变更共同传递的信号是2.x 系列在持续跟进 React 与 TS 生态的类型规范演进。例如SFC→FC、StatelessComponent→FunctionComponent的替换本质上是因为 React 官方认为无状态组件的说法已不准确组件可以有内部状态类型定义也随之更名。3.2 ArrayHelpers 泛型化让数组操作类型安全2.3.0 / 2.3.32.3.0 为ArrayHelpers接口及其方法加入了 TypeScript 泛型使用FieldArray的开发者可以为数组指定元素类型并获得类型安全的数组工具方法// 2.3.0 之前数组方法缺乏元素类型约束 FieldArray namefriends {({ push, remove }) ( // push(anything) 都能通过编译 )} /FieldArray// 2.3.0 之后可声明数组元素类型 FieldArray{ name: string } namefriends {({ push, remove }) ( // push 仅接受 { name: string } )} /FieldArray2.3.3 进一步修正any[]是泛型的默认数组类型且每个方法push、remove、insert、swap、move、replace、unshift等都可独立覆盖数组元素类型。同时为了保证向后兼容泛型参数是可选的——不传类型的既有 TS 代码无需任何改动即可继续工作。字段类型定义可参考 packages/formik/src/types.tsx 中的ArrayHelpers与FieldArrayConfig。四、函数式状态更新对齐 React 的 setState 心智模型4.1 setValues 支持函数回调2.2.02.2.0 是一个值得关注的 Minor 版本setValues现在可以接受函数作为参数暴露了React.SetStateAction的能力// 2.2.0 之前只能传入完整对象依赖外部闭包中的 state容易出现 stale props 问题 setValues({ ...prevValuesFromClosure, email: newexample.com }); // 2.2.0 之后以函数形式基于最新状态派生新值 setValues(prevValues ({ ...prevValues, email: newexample.com }));从 Formik.tsx 的源码实现可以看到setValues内部通过isFunction(values) ? values(state.values) : values解析入参即传入函数时会以当前 reducer 状态中的最新 values作为参数调用它。这彻底解决了闭包捕获旧值这一 React 状态管理的经典痛点。4.2 setFieldValue 的函数式扩展2.4.72.4.7 将同样的能力下沉到单字段维度setFieldValue的值参数也可以是一个接收上一字段值的函数// 2.4.7 之后基于上一字段值派生新值 setFieldValue(count, prev (prev ?? 0) 1);对应源码 Formik.tsx 中const resolvedValue isFunction(value) ? value(getIn(state.values, field)) : value;即通过getIn(state.values, field)取到字段当前值并传入回调。同时 2.3.2 更新了setFieldValue的类型签名使其正确反映返回值是一个 Promise 且可能抛出错误——这提醒使用者setFieldValue在触发校验时会返回异步结果应当用await或.catch处理。五、Yup 集成深化transforms 与跨字段校验Formik 与 Yup 的深度集成是 2.x 后期演进的重点之一涉及两个 Minor 版本。5.1 支持 Yup transforms2.4.02.4.0 为validationSchema增加了对 Yup 解析转换transforms 的支持。Yup 的 transform 允许在值通过校验前对其进行类型转换如字符串数字转数字、去除首尾空格。此变更使 Formik 能正确处理经过 transform 后的值。5.2 $ 前缀跨字段校验2.3.02.3.0 附带了一个极具实用价值的 Yup 能力增强。Yup 默认只允许在同一字段对象内进行跨字段引用这在深层嵌套 schema 中会失效// 这段代码不工作object.nestedField 位于 object2 之外ref 无法解析 const deepNestedSchema Yup.object({ object: Yup.object({ nestedField: Yup.number().required(), }), object2: Yup.object({ nestedFieldWithRef: Yup.number() .min(0) .max(Yup.ref(object.nestedField)), // ✗ 无法引用兄弟对象中的字段 }), });而 Yup 提供的context特性以$前缀启用可以在整个 schema 范围内进行跨字段引用const deepNestedSchema Yup.object({ object: Yup.object({ nestedField: Yup.number().required(), }), object2: Yup.object({ // ✓ 使用 $ 前缀 context 特性任意位置的字段均可引用 nestedFieldWithRef: Yup.number() .min(0) .max(Yup.ref($object.nestedField)), }), });自 2.3.0 起在 Formik 的validationSchema中使用$前缀即可对整个 schema 中的任意字段进行交叉校验无论其嵌套位置如何。仓库的 packages/formik/test/yupHelpers.test.ts 提供了对应校验辅助函数的测试覆盖docs/guides/validation.md 也有 Yup 集成的配套说明。5.3 Yup transform 支持的短期回退2.4.1值得注意的是2.4.1 曾暂时回退了 Yup transform 支持Revert Yup transform support for the time being, this may be re-introduced in a future release under an opt-in prop。这表明 transform 的完整支持存在行为边界问题官方选择谨慎处理计划未来以可选 prop的方式重新引入。使用 transform 时应当留意版本差异2.4.0 可用2.4.12.4.9 中处于回退状态。六、FieldArray 性能优化从激进优化到审慎回退FieldArray 是 Formik 中处理动态数组字段的核心组件实现见 packages/formik/src/FieldArray.tsx其渲染性能直接决定复杂表单的流畅度。2.2.10 与 2.4.1 两个版本围绕它展开了优化→回退的完整循环是理解 React 性能优化边界的好案例。6.1 引入 shouldComponentUpdate 优化2.2.102.2.10 为FieldArray增加了shouldComponentUpdate检查用于避免不必要的重渲染——当 Formik 状态未变化时跳过更新提升包含大量数组字段的表单性能。6.2 优化被回退2.4.1但 2.4.1 果断回退了这个优化理由写得很直白RevertFieldArrayshouldComponentUpdate performance optimization. As it turns out, its a common use case to have JSX controlled via non-Formik state/props inside ofFieldArray, so its not safe to cancel re-renders here.即在FieldArray内部使用非 Formik 管理的 state/props 来控制 JSX 是非常常见的用法例如根据外部状态渲染条件内容强行取消重渲染会导致这类 UI 无法更新。这个案例的启示是性能优化不能以牺牲正确性为代价面对渲染劫持类优化必须充分评估用户的使用模式。6.3 数组字段错误处理的行为修复2.2.10同版本还修复了数组字段错误状态的细节错误随删除操作消失当数组字段存在错误、又通过arrayHelpers.remove等方式清空时字段错误从先前的[undefined]修正为undefined错误拆分逻辑修复此前数组字段错误会被错误地按字符拆分成数组现已修正为按错误字符串数组处理此前[error1, error2]可能被拆成单个字符数组。这两项修复保证了errors.friends这类数组错误结构的正确性建议在 packages/formik/test/FieldArray.test.tsx 中查看相关断言。七、校验机制的演进优先级调度与竞态修复2.2.x 系列围绕低优先级校验low priority validation做了一轮密集的工程迭代这是 Formik 内部调度机制的重要演进。7.1 校验优先级机制的出现2.2.1修复 scheduler 与validateFormWithLowPriority方法未正确调度的问题由 wellyshen 贡献2.2.2修复低优先级校验的竞态条件race condition2.2.3修复低优先级校验与浏览器密码自动填充的兼容问题2.2.4setFieldTouched改为高优先级校验2.2.5彻底移除低优先级校验实现Remove low-priority validation implementation并修复 botched TypeScript 构建中包含 scheduler 类型的问题。从 Formik.tsx 源码可以看到setTouched、setValues等 API 在shouldValidate未显式指定时分别默认跟随validateOnBlur与validateOnChange并调用validateFormWithHighPriority执行校验。这印证了 2.2.4 的改动方向与用户交互直接相关blur/touch的校验需要高优先级保证即时反馈而低优先级调度因复杂度过高在 2.2.5 被移除整体设计回归简洁。7.2 validateOnMount 竞态与 validateField 深路径修复2.2.7修复validateOnMount使用时的错误更新竞态回归问题2.2.10修复validateFieldAPI 对deep.dot.path深路径字段引用的校验问题对应 Formik.tsx 中validateField通过getIn(state.values, name)精确取值、通过fieldRegistry注册表找到字段级校验函数的实现。八、行为细节修复initialValues 深克隆与 getIn 语义8.1 initialValues 深克隆2.4.62.4.6 修复了一个隐蔽的引用污染问题Changing the state inside formik was changing reference of initialValues provided via props, deep cloning the initialvalues will fix it.此前 Formik 内部的setIn采用浅拷贝路径链见 utils.ts 的实现注释早期版本因使用cloneDeep导致嵌套更新时直接变异父对象但传入的props.initialValues本身需要深克隆否则表单状态变更会反向污染外部传入的初始值对象。修复后 Formik.tsx 在初始化 state 时统一使用cloneDeep(props.initialValues)等操作确保 props 与内部状态彻底隔离。8.2 getIn 对falsy 中间值的处理2.3.22.3.2 修正了工具函数getIn的边界语义实现见 utils.tsChangedgetInto return undefined when it cant find a value AND a parent of that value is falsy ( / 0 / null / false)即当路径中间的某个父级值为 falsy、0、null、false且无法继续下钻时getIn返回undefined而不是继续返回 falsy 父值。这让表单字段读取如values.a.b中a为空字符串的行为更可预期避免出现父值为空字符串却意外拿到值的异常场景。相关断言可参考 packages/formik/test/utils.test.tsx。8.3 其他值得注意的行为变更2.2.7form action空字符串允许显式设置form action。此前传入空字符串会导致 noop仅向 URL 追加#现在会正常提交到当前页面——Form组件的渲染逻辑见 packages/formik/src/Form.tsx2.2.7 多选 select修复getSelectedValues在元素无 options 时被调用的问题2.2.10 Field 无限循环修复Field在React.useEffect中使用 field helperssetTouched等作为参数时可能触发的无限循环2.3.1 enableReinitialize 无限循环修复initialValues变化但enableReinitialize未开启时的潜在无限循环场景对应 Formik.tsx 中enableReinitialize分支逻辑2.4.4 自定义组件 className 转发Field使用自定义组件时正确转发classNameprop相关实现见 packages/formik/src/Field.tsx2.4.4 resetForm 依赖修复修复resetForm函数依赖问题避免闭包捕获过期状态。九、工程与依赖层面的演进9.1 无副作用声明与类型依赖补全2.4.52.4.5 在 packages/formik/package.json 中标记sideEffects: false让 webpack、Rollup 等打包器可以对 Formik 执行更激进的 tree-shaking减小最终产物体积同时补充了缺失的types/hoist-non-react-statics类型依赖。9.2 安全依赖升级2.2.92.2.9 将lodash与lodash-es升级到最新版本属于安全与维护性升级。Formik 内部大量使用 lodash 的工具函数clone、cloneDeep、toPath、isEqual等见 utils.ts 顶部导入因此 lodash 的安全版本对运行时健壮性至关重要。十、如何验证与跟进这些变更你可以通过以下方式在当前仓库中验证本文所述的每一项变更阅读变更原文packages/formik/CHANGELOG.md 保留完整的逐版本记录含 PR 编号与提交哈希可直接跳转 GitHub 查看 diff对照源码实现核心逻辑集中在 packages/formik/src 目录如setValues/setFieldValue函数式更新见 Formik.tsxgetIn/setIn见 utils.ts运行测试在packages/formik目录执行npm test内部为tsdx test --envjsdom见 packages/formik/package.json可运行 packages/formik/test 下针对Field、FieldArray、utils、yupHelpers的回归测试迁移参考从 2.x 早期版本升级时建议重点核对本文涉及的函数式更新、Yup$前缀校验与类型泛型这三类行为差异配合 packages/formik/MIGRATING-v2.md 与根目录 MIGRATING-v2.md 使用。结语纵览 packages/formik/CHANGELOG.md 的 28 个版本可以清晰地看到一条演进主线2.x 系列在 API 层面高度稳定工程重心放在生态兼容React 19、TS 新规范、类型安全泛型与类型修正、校验正确性跨字段、竞态、深路径以及审慎的性能优化FieldArray 优化与回退上。对于正在使用 Formik 2.x 的团队这份变更日志既是升级决策的依据也是理解表单框架在真实场景中会遇到哪些边界问题的最佳实践教材。【免费下载链接】formikBuild forms in React, without the tears 项目地址: https://gitcode.com/gh_mirrors/fo/formik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表