Хук проверки формы реагирования вызывает бесконечный цикл повторного рендеринга - PullRequest
0 голосов
/ 13 апреля 2020

Я проверил другие вопросы, они мне не помогли

Я построил ловушку React для проверки входных данных моей формы. Однако это вызывает бесконечную повторную визуализацию l oop. Я отследил проблему до массива зависимостей useEffect. Когда я исключаю зависимость validators, она прекрасно работает! Я не буду когда-либо менять validators во время выполнения, так что эта пропа не нужна в массиве зависимостей? Мой ESLint react-hooks-plugin продолжает предупреждать меня, что зависимость validators отсутствует. Пожалуйста, помогите мне. Можно ли исключить зависимость validators из массива зависимостей useEffect, если я не буду изменять ее во время выполнения? Вот мой хук и мой компонент формы:

import { useState, useEffect } from "react";

function setObjectValues<
  K extends { [key: string]: unknown },
  T
>(
  object: K,
  value: T
): { [key in keyof K]?: T } {
  const initialResults: {
    [key in keyof K]?: T;
  } = {};
  for (const key in object) {
    initialResults[key] = value;
  }
  return initialResults;
}

export function useValidation<
  K,
  T extends {
    [key: string]: (value: K) => boolean;
  }
>(
  value: K,
  validators: T
): {
  valid: boolean;
  results: { [key in keyof T]?: boolean };
} {
  const [results, setResults] = useState<
    { [key in keyof T]?: boolean }
  >(setObjectValues(validators, true));

  useEffect(() => {
    const newResults: {
      [key in keyof T]?: boolean;
    } = {};

    for (const key in validators) {
      const valid = validators[key](value);
      newResults[key] = valid;
    }

    setResults(newResults);
  }, [value, validators]);

  const valid = Object.values(results).every(
    (item) => item === true
  );

  return { valid, results };
}

Мой компонент:

import { NextPage } from "next";
import {
  useFirebase,
  useValidation,
} from "app/hooks";
import {
  useState,
  useCallback,
  FormEvent,
} from "react";
import { useRouter } from "next/router";

type InputType = "email" | "password";

const SignUp: NextPage = () => {
  const firebase = useFirebase();
  const router = useRouter();

  const [email, setEmail] = useState("");
  const {
    valid: emailValid,
    results: emailValidationResults,
  } = useValidation(email, {
    containsAt: (value) => value.includes("@"),
  });

  const [password, setPassword] = useState("");
  const {
    valid: passwordValid,
    results: passwordValidationResults,
  } = useValidation(password, {
    isLongEnough: (value) => value.length >= 8,
    containsLowerCase: (value) =>
      value.toUpperCase() !== value,
    containsUpperCase: (value) =>
      value.toLowerCase() !== value,
    containsNumber: (value) => /\d/.test(value),
  });

  const handleSubmit = useCallback(
    (event: FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      if (
        emailValid === true &&
        passwordValid === true &&
        email !== "" &&
        password !== ""
      ) {
        const error = firebase.createUser(
          email,
          password
        );
        if (error) {
          console.warn(error.code);
        } else {
          router.push("/");
        }
      } else {
        console.warn("Invalid user values");
      }
    },
    [
      email,
      emailValid,
      firebase,
      password,
      passwordValid,
      router,
    ]
  );

  console.log(emailValid, passwordValid);

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email</label>
      <input
        value={email}
        onChange={(event): void =>
          setEmail(event.target.value)
        }
        id="email"
        placeholder="Email"
      />
      <p>{emailValid}</p>
      <label htmlFor="password">Password</label>
      <input
        value={password}
        onChange={(event): void =>
          setPassword(event.target.value)
        }
        id="password"
        placeholder="Password"
      />
      <p>{passwordValid}</p>
      <button type="submit">Submit</button>
    </form>
  );
};

export default SignUp;

1 Ответ

1 голос
/ 13 апреля 2020
const {
    valid: passwordValid,
    results: passwordValidationResults,
  } = useValidation(password, {
    isLongEnough: (value) => value.length >= 8,
    containsLowerCase: (value) =>
      value.toUpperCase() !== value,
    containsUpperCase: (value) =>
      value.toLowerCase() !== value,
    containsNumber: (value) => /\d/.test(value),
  });

Здесь validators - это фактически объект, созданный во время Signup рендеринга. Когда вы создаете новый объект, это всегда новый, другой объект, даже если значения внутри него могут быть одинаковыми. По этой причине добавление его в массив зависимостей приводит к бесконечным повторным визуализациям.

Если объект не зависит от состояния или свойств вашего компонента, переместите объявление за пределы компонента, чтобы оно только создавалось один раз.

// outside SignUp
const validators = {
    isLongEnough: (value) => value.length >= 8,
    containsLowerCase: (value) =>
      value.toUpperCase() !== value,
    containsUpperCase: (value) =>
      value.toLowerCase() !== value,
    containsNumber: (value) => /\d/.test(value),
  };

// inside SignUp
const {
    valid: passwordValid,
    results: passwordValidationResults,
  } = useValidation(password, validators);

Я бы предложил сохранить validators в массиве зависимостей, потому что он все равно должен работать. Оставляя это большую часть времени, это запах кода. В большинстве случаев, если validators изменилось, вы захотите повторно запустить эффект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...