1
import React, {
  useState
} from "react";
export default function DropDown() {
  const [mail, useEmail] = useState("");
  const [disabled, useDisabled] = useState(false)
  const handleChange = (e) => {
    useEmail(e.target.value)
  }
  if (mail.includes('s@gmail.com')) {

    React.useEffect(() => {
      setInterval(() => {
        useDisabled(true);
      }, 2000);
    })
  } else {
    useDisabled(false)
  }
  return (
    <>
      <input 
        value={ mail }
        onChange={ handleChange }
        maxLength="20" 
      />
      <button 
        disabled={ mail.length < 1 }
      > Submit </button>
    </>
  );
}

but at the time it throws error like "Too many re-renders. React limits the number of renders to prevent an infinite loop." how to resolve that error?

DBS
  • 9,110
  • 4
  • 35
  • 53
Sougata Mukherjee
  • 547
  • 1
  • 8
  • 22
  • It is tradition to name the set function returned by `useState` as `setSomething` not `useSomething`. React names hooks `useSomething` so you violating that convention makes your code really hard to follow. – Quentin Nov 29 '21 at 15:18
  • Why are you setting `disabled` to `true` with `useInterval`? – Quentin Nov 29 '21 at 15:18
  • You are also calling your `useEffect` inside the if function. Never call Hooks inside loops, conditions or nested functions. – Giacomo Nov 29 '21 at 15:19
  • when existing email user type that time button should be disabled or else useDisabled button false value return – Sougata Mukherjee Nov 29 '21 at 15:23

2 Answers2

4

TL.DR:

import React, { useState } from "react";
export default function DropDown() {
    const [mail, setEmail] = useState("");

    const handleChange = (e) => {
        setEmail(e.target.value);
    };

    return (
        <>
            <input value={mail} onChange={handleChange} maxLength={20} />
            <button disabled={mail.includes("s@gmail.com") || mail.length < 1}> Submit </button>
        </>
    );
}

Let's break it down:

  1. Hooks (useState, useEffect, etc) cannot be conditionally rendered, so you cannot write them inside if else statements.
  2. const [mail, setMail] = useState(""). The second attribute should be setSomething, not useSomething. The useSomething should be reserved to the declarations of new hooks. The setMail returned by the useState is not a hook, but a normal function (the useState itself is the hook).
  3. setState (or setDisabled, in your example) should never be used directly inside the render of a component. A setState causes the component to re-render so, if when re-rendering a setState is encountered, it will trigger another re-render, that will trigger another setState, and so on (leading to the ""Too many re-renders." you had). Generally speaking, setState should only be used on event handlers (such as onClick, onKeyDown, onSomethingElseHapped, etc)

There are a few more minor issues with your code. I encourage you to go through the basics of react one more time.

juliomrc
  • 596
  • 3
  • 8
  • 1
    Why not call setDisabled inside handleChange instead of useEffect? – Alexander Vidaurre Arroyo Nov 29 '21 at 15:29
  • thanks a lot. it's now working fine – Sougata Mukherjee Nov 29 '21 at 15:29
  • @AlexanderVidaurreArroyo you are absolutely right. Edited. – juliomrc Nov 29 '21 at 15:30
  • Yes perfect @juliomrc, Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React functional component. We can check more depth details [here](https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level) – Dhaval Vachheta Nov 29 '21 at 15:31
  • @juliomrc I think that states changes are asynchronous, so is possible that `mail` is not up to date when `mail.includes("s@gmail.com") || mail.length < 1` gets evaluated. Try assign `const mail = e.target.value` first – Alexander Vidaurre Arroyo Nov 29 '21 at 15:42
  • As of today, unless inside a `async function`, react batches the `setStates`. https://stackoverflow.com/questions/53048495/does-react-batch-state-update-functions-when-using-hooks There is no need to keep the `disabled` in state though, so on that part you are correct. – juliomrc Nov 29 '21 at 15:47
  • 2
    No need to use a state for disabled. You can simply add a constant on each render. unnecessary create a state. will retrigger render. – xdeepakv Nov 29 '21 at 15:48
3

You won't need to create a state for the disabled. You can simply validate using it on every render. The sample is given below. Whenever you update input, the component will retrigger and validation will be performed.

import { useState } from "react";
import "./styles.css";
const blackListed = /^s@gmail\.com$/;
const emailReg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;

const isEmailValid = (email = "") => {
  if (!email.length) return false;
  if (!emailReg.test(email)){
    return false
  }
  return !blackListed.test(email);
};
export default function App() {
  const [mail, setEmail] = useState("");
  const handleChange = ({ target: { value } }) => {
    setEmail(value);
  };
  const isValidEmail = isEmailValid(mail);
  return (
    <div>
      <input value={mail} onChange={handleChange} maxLength="20" />
      <button disabled={!isValidEmail}> Submit </button>
    </div>
  );
}

If you want to clear email after a certain time. You can use useEffect with setTimeout and watch for change.

 useEffect(() => {
    const id = setTimeout(() => {
      if (!isValidEmail && mail.length) {
        alert("Your email is invalid");
        setEmail("")
      }
    }, 1000)
    return () => clearTimeout(id);
  }, [isValidEmail])

Complete code:

import { useState, useEffect } from "react";
import "./styles.css";
const blackListed = /^s@gmail\.com$/;
const emailReg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;

const isEmailValid = (email = "") => {
  if (!email.length) return false;
  if (!emailReg.test(email)) {
    return false
  }
  return !blackListed.test(email);
};


export default function App() {
  const [mail, setEmail] = useState("");
  const handleChange = ({ target: { value } }) => {
    setEmail(value);
  };
  const isValidEmail = isEmailValid(mail);

  // Clear after some toat and message.
  useEffect(() => {
    const id = setTimeout(() => {
      if (!isValidEmail && mail.length) {
        alert("Your email is invalid");
        setEmail("")
      }
    }, 1000)
    return () => clearTimeout(id);
  }, [isValidEmail])

  return (
    <div>
      <input value={mail} onChange={handleChange} maxLength="20" />
      <button disabled={!isValidEmail}> Submit </button>
    </div>
  );
}

Working code:

https://codesandbox.io/embed/nervous-cache-tm5yt?fontsize=14&hidenavigation=1&theme=dark

xdeepakv
  • 7,835
  • 2
  • 22
  • 32