如何优雅的处理 react 表单-编程思维

如何优雅的处理 React 表单

HTML 表单处理本身是一件比较简单的事情,但是当我们对交互的要求高了之后,他就会变得异常复杂——尤其是在 React 中使用时,我们不得不创建冗长的代码去维护各种状态。

那么有没有什么现成的开源方案可以供我们使用,最终优雅的创建 React 表单呢?

本文不会详细阐述每个库的用法、api,只是向大家介绍整合方案

如果我们直接写代码,大概率表单组件最后会是这样:

const [inputVal, setInputVal] = useState("");
const [errMessage, setErrMessage] = useState(null);
const isError = useMemo(() => Boolean(errMessage), [errMessage]);

function validate(val) {
  if (!val) {
    return "xxxx";
  }

  if (val.length > 10) {
    return "xxxx";
  }

  if (val.length < 2) {
    return "xxx";
  }

  return null;
}

function handleChange(e) {
  const value = e.target.value;
  setInputVal(value);

  const errMsg = validate(value);
  setErrMessage(errMsg);
}

return (
  <div>
    <input onChange={handleChange} />
    {isError && <p>{errMessage}</p>}
  </div>
);

我们仅仅只是校验了一个变量的长度和存在,就已经用了这么多的代码,还要处理三个状态!这太反人类了。

因此我们可以尝试使用如下四个库:react-hook-form, @hookform/resolvers, joi, joi-messages-zh_cn

优雅的表单处理

react-hook-form 可以非常方便的为我们创建一个表单,拥有更少的代码、更好的性能,并且可以配合所有的第三方库。

import { useForm } from "react-hook-form";

export default function App() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm();
  const onSubmit = (data) => console.log(data);

  console.log(watch("example"));

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input defaultValue="test" {...register("example")} />

      <input {...register("exampleRequired", { required: true })} />
      {errors.exampleRequired && <span>This field is required</span>}

      <input type="submit" />
    </form>
  );
}

配合 Material-UI:

import Select from "react-select";
import { useForm, Controller } from "react-hook-form";
import Input from "@material-ui/core/Input";

const App = () => {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      firstName: "",
      select: {},
    },
  });
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="firstName"
        control={control}
        render={({ field }) => <Input {...field} />}
      />
      <Controller
        name="select"
        control={control}
        render={({ field }) => (
          <Select
            {...field}
            options={[
              { value: "chocolate", label: "Chocolate" },
              { value: "strawberry", label: "Strawberry" },
              { value: "vanilla", label: "Vanilla" },
            ]}
          />
        )}
      />
      <input type="submit" />
    </form>
  );
};

优雅的校验

joi 是一个用来校验 JavaScript 类型的库。

我们可以定义一个 Schema:

const schema = Joi.object({
  username: Joi.string().max(12).min(2).required(),
  password: Joi.string().min(6).required(),
});

通过 @hookform/resolvers 库我们可以将 schema 传递给 react-hook-form 用来校验表单数据。

但现在有一个问题,那就是所有的报错都是英文,该如何切换为中文呢?

这就需要用到 Joi 的 defaults 方法了。

// my_joi.js
import Joi from "joi";
import cnMessages from "joi-messages-zh_cn";

export default Joi.defaults((schema) =>
  schema.options({
    messages: { ...cnMessages["zh-cn"] },
  })
);

messages 是我们的自定义消息,joi-message-zh_cn 这个库已经为我们写好了中文的报错消息。

最后,在定义 scheme 时,加上 .label('用户名'),就可以讲报错消息彻底改换为中文了。

最终效果

这里放一段使用 material ui 的例子:

// 这里使用 defaults 封装过的 Joi
const schema = Joi.object({
  username: Joi.string().max(12).min(2).required().label("用户名"),
  password: Joi.string().min(6).required().label("密码"),
});

function LoginForm({ onSubmit }) {
  const {
    control,
    handleSubmit,
    register,
    formState: { errors },
  } = useForm({
    defaultValues: {
      username: "",
      password: "",
    },
    resolver: joiResolver(schema),
  });

  function formError(field) {
    const err = errors[field];
    return {
      error: Boolean(err),
      helperText: err?.message,
    };
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)} className={styles.wrapper}>
      <Controller
        name="username"
        control={control}
        render={({ field }) => (
          <TextField
            {...field}
            {...register("username")}
            className={styles.item}
            variant="standard"
            label="用户名"
            required
            {...formError("username")}
          />
        )}
      />
      <Controller
        name="password"
        control={control}
        render={({ field }) => (
          <TextField
            {...field}
            {...register("password")}
            className={styles.item}
            variant="standard"
            type="password"
            label="密码"
            required
            {...formError("password")}
          />
        )}
      />
      <Button type="submit">提交</Button>
    </form>
  );
}

版权声明:本文版权归作者所有,遵循 CC 4.0 BY-SA 许可协议, 转载请注明原文链接
https://www.cnblogs.com/xhyccc/p/17400318.html