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

基于Formik和React的可复用自定义Input组件报错不显示问题排查

自定义Formik Input组件错误信息不显示问题排查

问题描述

我开发了一个可通过props接收数据的自定义基础表单Input组件,集成Formik使用时遇到错误信息无法显示的问题,请帮忙排查代码实现中的错误并解决。

原组件代码

/* eslint-disable react/prop-types */

const Input = (props) => {
  const {formik, icon, title,type,otherState,btn1,btn2, name} = props
  Input.defaultProps = {
    type: "text",
    otherState: null,
    btn1: null,
    btn2: null,
    name: title.toLowerCase()
  }

   if (otherState) {
     var {state, setState} = otherState
   }
    if (btn1) {
      var {icon1, text1, action1} = btn1
    }
    if (btn2) {
      var {icon2, text2, action2} = btn2
    }

  return (
   <div className="mb-2">
   <span className="flex">
     <img className="w-6 h-6 mr-2" src={icon} alt={title} />
     <label
       htmlFor={name}
       className="block text-sm font-semibold text-textPrimary dark:text-dark_textPrimary"
     >
       {title}
     </label>
   </span>
   <span className="flex w-full">
{console.log(formik.touched.name)}
   <input
     
     type={type}
     name={name}
     value={formik.values.name}
     title={`${formik.touched.name && formik.values.name == "" ? `${title} is required` : ""}`}
     onChange={formik.handleChange}
     onBlur={formik.handleBlur}
     placeholder={
       formik.touched.name && formik.errors.name
         ? `${title} is required`
         : `${title}`
     }
     className={`block w-full px-4 py-2 mt-2 text-textPrimary dark:text-dark_textPrimary bg-background dark:bg-dark_background border-2 rounded-md focus:ring-secondary ${formik.touched.name && formik.errors.name ? " border-error_red placeholder-error_red" : "border-cardBorder dark:border-dark_cardBorder"}`}
   />

     {
        btn1 || btn2 ? (
          <img
       className="w-6 h-7 cursor-pointer mt-3 transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-110"
       onClick={() => action1()}
       src={state ? icon1 : icon2}
       title={!state ? text1 : text2}
     />) : null
     }
   </span>
{
     formik.touched.name && formik.errors.name && formik.values.name != "" ? (
       <h3 className="text-xs text-error_red">{formik.errors.name}</h3>
     ) : null
   }
 </div>
  )
}

export default Input

组件调用示例

<Input
  formik={formik}
  icon={emailIcon}
  title="Email"
  name="email"
  type="email"
/>

<Input 
  formik={formik}
  icon={passwordIcon}
  name="password"
  title="Password"
  type={showPassword ? "password" : "text"}
  otherState={{'state':showPassword, 'setState':setShowPassword}}
  btn1={{'icon1':eyeOpenIcon, 'text1':"Hide Password", 'action1':() => setShowPassword(!showPassword)}}
  btn2={{'icon2':eyeCloseIcon, 'text2':"Show Password", 'action2':() => setShowPassword(!showPassword)}}
/>

错误排查与修复方案

核心错误点

  1. Formik字段属性访问错误:所有formik.touched.name、formik.values.name、formik.errors.name的写法都是错误的,这里的name是props传入的变量(比如"email"),必须用方括号语法访问动态属性,即formik.touched[name],否则会固定访问formik.touched.name这个不存在的属性。
  2. defaultProps定义位置错误:将Input.defaultProps放在组件函数内部,会导致每次组件渲染时都重新赋值,且不符合React规范,应该移到组件外部。
  3. 错误信息显示条件逻辑错误:原条件formik.values.name != ""会排除空值必填错误的显示场景,应该移除该条件,只要字段被触碰且有错误就显示。
  4. 变量声明不规范:使用var声明变量存在变量提升问题,建议改用let/const。
  5. 按钮交互逻辑漏洞:仅调用action1,未处理只有btn2的情况,且图片切换逻辑可以优化。

修复后的组件代码

/* eslint-disable react/prop-types */

const Input = (props) => {
  const { formik, icon, title, type, otherState, btn1, btn2, name } = props

  const { state, setState } = otherState || {}
  const { icon1, text1, action1 } = btn1 || {}
  const { icon2, text2, action2 } = btn2 || {}

  // 动态获取Formik相关字段状态
  const fieldTouched = formik.touched[name]
  const fieldValue = formik.values[name]
  const fieldError = formik.errors[name]

  return (
    <div className="mb-2">
      <span className="flex">
        <img className="w-6 h-6 mr-2" src={icon} alt={title} />
        <label
          htmlFor={name}
          className="block text-sm font-semibold text-textPrimary dark:text-dark_textPrimary"
        >
          {title}
        </label>
      </span>
      <span className="flex w-full">
        <input
          type={type}
          name={name}
          value={fieldValue || ''}
          title={`${fieldTouched && !fieldValue ? `${title} is required` : ""}`}
          onChange={formik.handleChange}
          onBlur={formik.handleBlur}
          placeholder={
            fieldTouched && fieldError
              ? `${title} is required`
              : `${title}`
          }
          className={`block w-full px-4 py-2 mt-2 text-textPrimary dark:text-dark_textPrimary bg-background dark:bg-dark_background border-2 rounded-md focus:ring-secondary ${fieldTouched && fieldError ? " border-error_red placeholder-error_red" : "border-cardBorder dark:border-dark_cardBorder"}`}
        />

        {(btn1 || btn2) && (
          <img
            className="w-6 h-7 cursor-pointer mt-3 transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-110"
            onClick={() => state ? action2?.() : action1?.()}
            src={state ? icon2 : icon1}
            title={state ? text2 : text1}
          />
        )}
      </span>
      {fieldTouched && fieldError && (
        <h3 className="text-xs text-error_red">{fieldError}</h3>
      )}
    </div>
  )
}

// 将defaultProps移到组件外部
Input.defaultProps = {
  type: "text",
  otherState: null,
  btn1: null,
  btn2: null,
  name: ''
}

// 处理name的默认值逻辑(原defaultProps无法访问title,所以新增包装组件)
const EnhancedInput = (props) => {
  const { title, name } = props
  return <Input {...props} name={name || title?.toLowerCase() || ''} />
}

export default EnhancedInput

额外说明

  • 原defaultProps中name: title.toLowerCase()的写法不生效,因为defaultProps无法访问其他props,所以新增了一层包装组件EnhancedInput来处理name的默认值逻辑。
  • 对按钮交互逻辑做了优化,根据state的值调用对应的action1或action2,并使用可选链运算符?.避免空值调用报错。
  • 统一提取了Formik字段的状态变量,让代码更清晰易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:24:50