【react】 form表单校验逻辑封装

好的,接下来我们将创建一个可复用的 React 表单校验封装组件。我们将使用 React 的状态和上下文功能来管理表单的校验状态和错误信息。我们还将实现一些常见的校验规则,并使其易于扩展。

实现步骤

  1. 创建一个 FormProvider 组件来提供表单上下文。
  2. 创建一个 useForm 钩子来使用表单上下文。
  3. 创建一个 useField 钩子来管理单个表单字段的状态和校验。
  4. 创建一些常见的校验规则。
  5. 创建示例表单来展示如何使用这些封装组件。

1. 创建 FormProvider.js

import React, { createContext, useContext, useState } from "react"

const FormContext = createContext()

export const FormProvider = ({ children, onSubmit }) => {
  const [fields, setFields] = useState({})
  const [checkedAll, setCheckedAll] = useState(false)

  const registerField = ({ name, label, validate, defaultValue }) => {
    setFields((prev) => ({
      ...prev,
      [name]: { name, label, value: defaultValue, validate, error: null }
    }))
  }

  const updateFieldValue = (name, value) => {
    const field = fields[name]
    if (field) {
      setFields((prev) => ({ ...prev, [name]: { ...prev[name], value } }))
    }
  }

  const setFieldError = (name, error) => {
    setFields((prev) => ({ ...prev, [name]: { ...prev[name], error } }))
  }

  const validateField = (name, value) => {
    const field = fields[name]
    if (field && field.validate) {
      const error = field.validate(value)
      setFieldError(name, error)
      return error
    }
    return null
  }

  const validateAllFields = (formData) => {
    let isValid = true
    Object.keys(formData).forEach((name) => {
      if (!!validateField(name, formData[name])) {
        isValid = false
      }
    })
    if (!isValid) {
      setCheckedAll(true)
    }
    return isValid
  }

  const handleSubmit = (event) => {
    event.preventDefault()
    const formData = Object.values(fields).reduce(
      (acc, field) => ({ ...acc, [field.name]: field.value }),
      {}
    )

    if (validateAllFields(formData)) {
      onSubmit(formData)
    }
  }

  return (
    <FormContext.Provider
      value={{
        registerField,
        validateField,
        updateFieldValue,
        checkedAll
      }}
    >
      <form onSubmit={handleSubmit}>{children}</form>
    </FormContext.Provider>
  )
}

export const useForm = () => {
  return useContext(FormContext)
}

2. 创建 useField.js

import { useState, useEffect } from "react"
import { useForm } from "./FormProvider"

const useField = ({ name, label, validate, defaultValue }) => {
  const { registerField, validateField, updateFieldValue, checkedAll } =
    useForm()
  const [value, setValue] = useState(defaultValue)
  const [error, setError] = useState(null)

  useEffect(() => {
      // 将 field 中的全部属性注入到 FormContext 管理
      registerField({ name, label, validate, defaultValue })
  }, [])

  useEffect(() => {
  	// 当 value 变化时,同步更新 field 中的 value 字段值
    updateFieldValue(name, value)
  }, [value])

  useEffect(() => {
    // 点击 submit 提交按钮,全局检测如果校验失败,显示错误信息
    if (validate && checkedAll) {
      const error = validateField(name, value)
      setError(error)
    }
  }, [validate, checkedAll])

  const handleChange = (e) => {
    const newValue = e.target.value
    setValue(newValue)
  }

  const handleBlur = () => {
    const error = validateField(name, value)
    setError(error)
  }

  return { value, error, handleChange, handleBlur }
}

export default useField

3. 创建一些常见的校验规则

// src/validationRules.js
export const required = (value) => {
  return value ? null : 'This field is required';
};

export const minLength = (min) => (value) => {
  return value.length >= min ? null : `Minimum length is ${min}`;
};

export const email = (value) => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(value) ? null : 'Invalid email address';
};

4. 创建示例表单 MyForm.js

import React from 'react';
import { FormProvider } from './FormProvider';
import useField from './useField';
import { required, minLength, email } from './validationRules';

// 简单实现 Form 表单中的 TextIput 组件,可根据需要自行扩展其他组件
const TextInput = ({ name, label, validate, defaultValue = "" }) => {
  const { value, error, handleChange, handleBlur } = useField({
    name,
    label,
    validate,
    defaultValue
  })

  return (
    <div>
      <label>{label}</label>
      <input
        name={name}
        value={value}
        onChange={handleChange}
        onBlur={handleBlur}
      />
      {error && <span style={{ color: "red" }}>{error}</span>}
    </div>
  )
}

const MyForm = () => {
  const handleSubmit = (values) => {
    console.log('Form Submitted!', values);
  };

  return (
    <FormProvider onSubmit={handleSubmit}>
      <TextInput name="username" label="Username" validate={required} />
      <TextInput name="email" label="Email" validate={email} />
      <TextInput name="password" label="Password" validate={minLength(6)} />
      <button type="submit">Submit</button>
    </FormProvider>
  );
};

export default MyForm;

5. 入口文件 index.js

import React from 'react';
import ReactDOM from 'react-dom';
import MyForm from './MyForm';

ReactDOM.render(
  <React.StrictMode>
    <MyForm />
  </React.StrictMode>,
  document.getElementById('root')
);

总结

通过以上步骤,我们实现了一个 React 表单校验封装,包括:

  1. 创建表单上下文并提供表单注册和校验功能。
  2. 创建自定义钩子来管理单个表单字段的状态和校验。
  3. 实现 TextInput 文本框的校验规则并在表单组件中使用。

这样可以轻松扩展和复用表单校验逻辑。你可以根据自己的需求增加更多的校验规则和表单字段类型。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐