简体   繁体   English

使用带有钩子的React.memo来控制输入

[英]Using React.memo with hooks for controlled inputs

I'm providing some form functionality from a custom React hook. 我从自定义的React钩子提供了一些表单功能。 This hook has some functionality similar to Formik (but this is really basic stuff). 这个钩子具有一些与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
  };
}



The form is the following: 形式如下:

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>
  );
}

And the memoized component is the next: 记忆的组件是下一个:

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);

I enter some text into the first input. 我在第一个输入中输入一些文本。 Then, when I switch to the second Input and start typing, the first input loses the value. 然后,当我切换到第二个Input并开始键入时,第一个Input失去了值。 It's like the form is not rendering the LAST MEMOIZED input, but prior versions instead. 就像表单没有呈现LAST MEMOIZED输入,而是呈现以前的版本。 I'm a React beginner and can't figure out the solution. 我是React的初学者,无法解决问题。 Any help please? 有什么帮助吗?

Try using the updater form of setState which takes a function: 尝试使用带有功能的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
  }));
}

Your areEqual method translates to 您的areEqual方法转换为

Re-render my Input ONLY when the value changes. 仅在value更改时才重新渲染我的输入。

But in reality, your handleChange function from the hook is also changing. 但实际上,挂钩中的handleChange函数也在发生变化。 Also, you use the same handleChange for both the inputs. 另外,您对两个输入使用相同的handleChange So, the Input "remembers" only the handleChange from the last time value had changed and since handleChange is tracking values via closure, it in-turn "remembers" the values when it was created. 因此, Input handleChange上次更改value起“记住” handleChange ,并且由于handleChange通过闭包跟踪values ,因此它在创建时依次“记住” values

Changing your areEqual method (or completely omitting it) to verify a change in handleChange , will solve your problem. 更改areEqual方法(或完全省略它)以验证handleChange的更改将解决您的问题。

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

A codesandbox of the solution here 该解决方案的codesandbox 这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM