react-hook-form使用

官网地址:https://react-hook-form.com/
react-hook-form是专门为校验表单、提交表单设计的,使用起来比传统的onChange、setState要方便很多。
而且它进一步做了优化,减少了不必要的render

image.png

安装

npm install react-hook-form

使用

import React from "react";
import { useForm, SubmitHandler } from "react-hook-form";

type Inputs = {
  example: string,
  exampleRequired: string,
};

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

  console.log(watch("example")) // watch input value by passing the name of it

  return (
    /* "handleSubmit" will validate your inputs before invoking "onSubmit" */
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* register your input into the hook by invoking the "register" function */}
      <input defaultValue="test" {...register("example")} />
      
      {/* include validation with required or other standard HTML validation rules */}
      <input {...register("exampleRequired", { required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}
      
      <input type="submit" />
    </form>
  );
}

我们项目中的使用

export default function AddressForm({ className }: AddressFormProps): React.ReactElement {
  const {
    formState: { errors },
    register,
  } = useContext(UseFormContext) as UseFormReturn;

  return (
    <div className={classnames("space-y-6", className)}>
      <Input
        placeholder="Address Name (ex. home)*"
        register={register("addressName", { required: "Address name is required" })}
        {...useFormProps({ name: "addressName", errors })}
      />
      <Input
        placeholder="Street Name*"
        register={register("streetName", { required: "Street Name is required" })}
        {...useFormProps({ name: "streetName", errors })}
      />
      <div className="grid grid-cols-2 gap-x-2 gap-y-6">
        <Input
          placeholder="Street Number*"
          register={register("streetNumber", { required: "Street Number is required" })}
          {...useFormProps({ name: "streetNumber", errors })}
        />
        <Input
          placeholder="Flat, Floor, Door"
          register={register("flat", { required: "Flat, Floor, Door is required" })}
          {...useFormProps({ name: "flat", errors })}
        />
        <Input
          placeholder="City*"
          register={register("city", { required: "City is required" })}
          {...useFormProps({ name: "city", errors })}
        />
        <Select
          options={states}
          placeholder={stateSelectPlaceHolder}
          register={register("state", { required: "State is required" })}
          {...useFormProps({ name: "state", errors })}
        />
        <Input
          placeholder="PinCode*"
          register={register("pinCode", { required: "Pin Code is required" })}
          {...useFormProps({ name: "pinCode", errors })}
        />
        <Input
          placeholder="Country*"
          register={register("Country", { required: "Country is required" })}
          {...useFormProps({ name: "country", errors })}
        />
        <Input
          placeholder="LandMark*"
          register={register("landMark", { required: "LandMark is required" })}
          {...useFormProps({ name: "landMark", errors })}
        />
      </div>
      <Toggle label={<span className="body">Set as default address</span>} />
    </div>
  );
}
 body={
        <>
          <UseFormContext.Provider value={useFormMethods}>
            <AddressForm className="mt-4" />
          </UseFormContext.Provider>
          <div className="hidden md:block md:mt-10">
            <CustomButton noMargin fullSize rounded primary label="SAVE" onClick={onSubmit} />
          </div>
        </>
      }
const onSubmit = useMemo(
    () =>
      handleSubmit(async (data) => {
        // TODO: Call API
        // eslint-disable-next-line no-console
        debugger;
        console.log(data);
        if (nextStepHref) {
          await router.push(nextStepHref);
          switch (nextStepHref.pathname) {
            case pathnames.settingsAddressTab:
              showNotifications({
                type: "default",
                title: "Address Changed",
                message: "You have successfully changed your address.",
              });
              break;
            default:
              break;
          }
        }
      }),
    [handleSubmit, nextStepHref, router, showNotifications]
  );

完全替代了原先需要在组件里面声明state来接受input的值。

监听功能

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

function App() {
  const { register, watch, formState: { errors }, handleSubmit } = useForm();
  const watchShowAge = watch("showAge", false); // you can supply default value as second argument
  const watchAllFields = watch(); // when pass nothing as argument, you are watching everything
  const watchFields = watch(["showAge", "number"]); // you can also target specific fields by their names

  const onSubmit = data => console.log(data);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input type="checkbox" {...register("showAge")} />
        
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && <input type="number" {...register("age", { min: 50 })} />}
        
        <input type="submit" />
      </form>
    </>
  );
}

单独组件监听

import React from "react";
import { useForm, useWatch } from "react-hook-form";

function IsolateReRender({ control }) {
  const firstName = useWatch({
    control,
    name: 'firstName', // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both
    defaultValue: 'default' // default value before the render
  });

  return <div>{firstName}</div>; // only re-render at the component level, when firstName changes
}

function App() {
  const { register, control, handleSubmit } = useForm();
  
  return (
    <form onSubmit={handleSubmit(data => console.log("data", data))}>
      <input {...register("firstName")} />
      <input {...register("last")} />
      <IsolateReRender control={control} />
      
      <input type="submit" />
    </form>
  );

默认值

很多时候,我们需要设置默认值,比如在编辑的时候,已经有原有的值了,这个时候怎么设置呢?

const useFormMethods = useForm<AddressInfo>({
    defaultValues: {
      streetAddress1: "dfsdds",
      villageArea: "dfds",
      houseNumber: "fsdf",
      flat: "fdsdf",
      townCity: "fdsd",
      provinceState: "China",
      pinCode: "dsffsd",
      country: "dffds",
      landMark: "fdsfds",
    },
  });

useFormContext 使用

表单在一个子组件里面,这时候就要用useFormContext了,
父组件:

import { useForm, FormProvider } from "react-hook-form";
const useFormMethods = useForm<AddressInfo>({
    defaultValues: {
      streetAddress1: "dfsdds",
      villageArea: "dfds",
      houseNumber: "fsdf",
      flat: "fdsdf",
      townCity: "fdsd",
      provinceState: "China",
      pinCode: "dsffsd",
      country: "dffds",
      landMark: "fdsfds",
    },
  });
 <FormProvider {...useFormMethods}>
            <AddressForm className="mt-4" onDefaultAddressChange={onDefaultAddressChange} />
          </FormProvider>

通过FormProvider 把useFormMethods传递给子组件
子组件接收使用:

import { useFormContext } from "react-hook-form";
 const {
    formState: { errors },
    register,
  } = useFormContext();
<Input
        placeholder="Address Name (ex. home)*"
        register={register("streetAddress1", { required: "Address name is required" })}
        {...useFormProps({ name: "streetAddress1", errors })}
      />

官方demo也挺好:

import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";

export default function App() {
  const methods = useForm();
  const onSubmit = data => console.log(data);

  return (
    <FormProvider {...methods} > // pass all methods into the context
      <form onSubmit={methods.handleSubmit(onSubmit)}>
        <NestedInput />
        <input type="submit" />
      </form>
    </FormProvider>
  );
}

function NestedInput() {
  const { register } = useFormContext(); // retrieve all hook methods
  return <input {...register("test")} />;
}

官方demo地址 https://github.com/react-hook-form/react-hook-form/tree/master/examples

 <Input
              label="Repeat your New Password"
              type="password"
              onChange={onRepeatPwdChange}
              register={register("cpassword", {
                required: "This field is required",
                validate: {
                  positive: (v) => {
                    const { newPassword } = getValues();
                    return newPassword === v || "Passwords should match!";
                    // if (v.indexOf(" ") !== -1)
                    //   return "Passwords should not contain Spaces";
                    // return true;
                  },
                },
              })}
              message={errors.cpassword?.message ?? ""}
              status={
                errors.cpassword?.message
                  ? FieldStatus.Error
                  : FieldStatus.Default
              }
            />
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容