react-hook-form:表单状态和输入状态之间双向映射的惯用方法

react-hook-form: idiomatic approach for two-way mapping between form state and input state

我目前正在为我的表单使用 Formik,但正在考虑切换到 react-hook-form。我担心的问题之一是输入的 DOM 状态与预期的 DOM 状态不同。一个示例是带有 type="number" 的输入,其 value 在 DOM 级别是一个字符串。所以我可能想将一个字符串映射到一个数字。在 Formik 中:

<input
  type="number"
  value={formikProps.values.myNumericField ?? ''}
  onChange={(event) => {
    formikProps.setFieldValue(
      'myNumericField',
      event.currentTarget.value === '' ? null : parseInt(event.currentTarget.value)
    );
  }}
/>

在 react-hook-form 中执行此操作的惯用方法是什么?

您可以改用受控输入并将其包装在 react-hook-formController 中。

首先,创建您常用的受控输入,它至少需要 2 个道具,valueonChange,并通知家长有关更改:

onst NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(({
  onChange,
  ...inputProps
}, ref) => {
  const handleChange = useCallback((e) => { 
    const { value } = e.target;
    const parsedValue = parseInt(value, 10);

    // If the value is not a number, we don't want to clear the input. Instead, 
    // just use the original string value and handle validation outside the input.
    // You can be more strict about this by passing 0 or '' instead and changing
    // the type attribute below to "number".
    onChange(value === `${ parsedValue }` ? parsedValue : value);
  }, [onChange]);

  return (
    <input
      ref={ ref }
      type="text"
      onChange={ handleChange }
      { ...inputProps } />
  );
});

要将此组件与 react-hook-form 一起使用,只需将其包装在 Controller 组件中,该组件将传递 onChangeonBlurvalueref 到您的组件:

import ReactDOM from "react-dom";
import React, { useCallback } from "react";
import { useForm, Controller } from "react-hook-form";

const App: React.FC = () => {
  const { handleSubmit, control, watch } = useForm();

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Controller
        as={NumberInput}
        control={control}
        name="number"
        defaultValue=""
      />

      <pre>{ JSON.stringify(watch(), null, "  ") }</pre>

      <button type="submit">Submit</button>
    </form>
  );
};

const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);