如何通过HOC返回带内联包装的组件并实现Formik状态透传?
问题描述
我想要创建一个输入字段组件:当它处于<Formik>组件内部时,作为Formik Field生效;当没有Formik上下文时,则作为普通输入字段使用。目前我已通过高阶组件(HOC)结合useFormikContext和useField实现了该基础功能,但遇到了新问题:我希望能将字段的错误信息等数据传递给组件的使用处,比如当字段存在错误时,能在使用包装后的组件时获取这些状态。
现有代码
withField.tsx (HOC)
import { useField, useFormikContext } from "formik"; export const withField = (WrappedComponent: React.ComponentType) => { const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component"; const ComponentWithOrWithoutFormik = (props: any) => { const formikContext = useFormikContext(); if (formikContext) { const [field, meta] = useField(props); return ( <WrappedComponent {...field} {...props} onChange={(e: any) => { field.onChange(e); props.onChange && props.onChange(e); }} onBlur={(e: any) => { field.onBlur(e); props.onBlur && props.onBlur(e); }} /> ); } return <WrappedComponent {...props} />; }; ComponentWithOrWithoutFormik.displayName = `withOrWithoutFormik(${displayName})`; return ComponentWithOrWithoutFormik; };
组件定义与当前使用方式
// 定义CheckBoxField const CheckBoxField = withField((props) => <input {...props} />); // 当前使用方式 <CheckBoxField id={`id_${name}`} type="checkbox" name={name} />
期望使用方式
<CheckBoxField id={`id_${name}`} type="checkbox" name={name}> {({error, touched})=>( // 在这里处理错误提示等逻辑 {touched && error && <span className="error-text">{error}</span>} )} </CheckBoxField>
我知道普通组件可以通过返回children函数实现这种模式,但不清楚如何在HOC中适配。
解决方案
要实现函数式children传递状态的需求,需要修改HOC逻辑,让它能把Formik的meta数据(错误、触达状态等)传递给组件,同时支持调用传入的children函数。
修改后的withField.tsx
import { useField, useFormikContext } from "formik"; import React from "react"; export const withField = (WrappedComponent: React.ComponentType) => { const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component"; const ComponentWithOrWithoutFormik = (props: any) => { const formikContext = useFormikContext(); // 无Formik上下文时的默认状态 let meta = { error: undefined, touched: false, value: undefined }; let fieldProps = {}; if (formikContext) { const [field, formikMeta] = useField(props); meta = formikMeta; // 合并Formik的字段事件与自定义事件 fieldProps = { ...field, onChange: (e: any) => { field.onChange(e); props.onChange && props.onChange(e); }, onBlur: (e: any) => { field.onBlur(e); props.onBlur && props.onBlur(e); }, }; } // 合并所有属性:基础props + Formik字段属性 + 状态数据 const mergedProps = { ...props, ...fieldProps, meta }; // 如果传入的children是函数,直接调用并传递状态数据 if (typeof props.children === 'function') { return props.children({ ...meta, // 按需传递其他属性 ...mergedProps, }); } // 否则渲染包装后的组件,传递所有合并属性 return <WrappedComponent {...mergedProps} />; }; ComponentWithOrWithoutFormik.displayName = `withOrWithoutFormik(${displayName})`; return ComponentWithOrWithoutFormik; };
调整CheckBoxField的实现
让CheckBoxField同时支持基础渲染和函数式children:
const CheckBoxField = withField(({ children, ...props }) => { // 如果存在函数式children,调用并传递状态 if (typeof children === 'function') { return children(props.meta); } // 否则渲染基础输入框 return <input {...props} />; });
使用示例
基础使用(无自定义逻辑)
<CheckBoxField id={`id_${name}`} type="checkbox" name={name} />
带错误提示的自定义逻辑
<CheckBoxField id={`id_${name}`} type="checkbox" name={name}> {({ error, touched }) => ( <div className="checkbox-group"> <input id={`id_${name}`} type="checkbox" name={name} /> <label htmlFor={`id_${name}`}>选项</label> {touched && error && <span className="error">{error}</span>} </div> )} </CheckBoxField>
内容的提问来源于stack exchange,提问作者thezeeshantariq
相关产品推荐
相关产品推荐

