Использование React.memo с хуками для контролируемых входов - PullRequest
1 голос
/ 27 мая 2019

Я предоставляю некоторые функциональные возможности формы из пользовательского хука React. Этот хук имеет некоторую функциональность, похожую на Formik (но это действительно базовый материал).

function useFormValidation(initialState, validate) {
  const [values, setValues] = React.useState(initialState);
  const [errors, setErrors] = React.useState({});
  const [isSubmitting, setSubmitting] = React.useState(false);

  React.useEffect(() => {
    if (isSubmitting) {
      const noErrors = Object.keys(errors).length === 0;
      if (noErrors) {
        console.log("authenticated!", values.email, values.password);
        setSubmitting(false);
      } else {
        setSubmitting(false);
      }
    }
  }, [errors]);

  function handleChange(event) {
    setValues({
      ...values,
      [event.target.name]: event.target.value
    });
  }

  function handleBlur() {
    const validationErrors = validate(values);
    setErrors(validationErrors);
  }

  function handleSubmit(event) {
    event.preventDefault();
    const validationErrors = validate(values);
    setErrors(validationErrors);
    setSubmitting(true);
  }

  return {
    handleSubmit,
    handleChange,
    handleBlur,
    values,
    errors,
    isSubmitting
  };
}



Форма следующая:

function Register() {
  const {
    handleSubmit,

    handleChange,
    handleBlur,
    values,
    errors,
    isSubmitting
  } = useFormValidation(INITIAL_STATE, validateAuth);
  // const [email, setEmail] = React.useState("");
  // const [password, setPassword] = React.useState("");

  return (
    <div className="container">
      <h1>Register Here</h1>
      <form onSubmit={handleSubmit}>
        <Input
          handleChange={handleChange}
          handleBlur={handleBlur}
          name="email"
          value={values.email}
          className={errors.email && "error-input"}
          autoComplete="off"
          placeholder="Your email address"
        />
        {errors.email && <p className="error-text">{errors.email}</p>}
        <Input
          handleChange={handleChange}
          handleBlur={handleBlur}
          value={values.password}
          className={errors.password && "error-input"}
          name="password"
          // type="password"
          placeholder="Choose a safe password"
        />
        {errors.password && <p className="error-text">{errors.password}</p>}
        <div>
          <button disabled={isSubmitting} type="submit">
            Submit
          </button>
        </div>
      </form>
    </div>
  );
}

И запомнившийся компонент следующий:

function Input({
  handleChange,
  handleBlur,
  name,
  value,
  className,
  autoComplete,
  placeholder,
  type
}) {

  return (
    <input
      onChange={handleChange}
      onBlur={handleBlur}
      name={name}
      value={value}
      className={className}
      autoComplete={autoComplete}
      placeholder={placeholder}
      type={type}
    />
  );
}

function areEqual(prevProps, nextProps) {
  console.log(`
    prevProps: ${JSON.stringify(prevProps.value)}
    nextProps: ${JSON.stringify(nextProps.value)}
  `);
  return prevProps.value === nextProps.value;
}
const useMemo = (component, propsAreEqual) => {
  return memo(component, propsAreEqual);
};
export default useMemo(Input, areEqual);

Я ввожу некоторый текст в первый ввод. Затем, когда я переключаюсь на второй ввод и начинаю печатать, первый ввод теряет значение. Это похоже на то, что форма не отображает вход LAST MEMOIZED, а вместо этого предыдущие версии. Я новичок в React и не могу найти решение. Любая помощь, пожалуйста?

Ответы [ 2 ]

2 голосов
/ 27 мая 2019

Попробуйте использовать форму обновления setState, которая принимает функцию:

function handleChange(event) {
  // event.target wont be available when fn is run in setState
  // so we save them in our own local variables here
  const { name, value } = event.target;

  setValues(prev => ({
    ...prev,
    [name]: value
  }));
}
0 голосов
/ 27 мая 2019

Ваш areEqual метод переводится как

Повторно отображать мой ввод ТОЛЬКО при изменении value.

Но на самом деле ваша handleChange функция с крючка тоже меняется. Кроме того, вы используете один и тот же handleChange для обоих входов. Таким образом, Input «запоминает» только handleChange с момента последнего изменения value и, поскольку handleChange отслеживает values через замыкание, он, в свою очередь, «запоминает» values, когда он был создано.

Изменение метода areEqual (или полное его отсутствие) для проверки изменения handleChange решит вашу проблему.

function areEqual(prevProps, nextProps) {
  return (
    prevProps.value === nextProps.value &&
    prevProps.handleChange === nextProps.handleChange
  );
}

Код и ящик решения здесь

...