Home > OS >  Why getting too many re-renders?
Why getting too many re-renders?

Time:02-12

I have a Calculator component for calculating the exponent of a given base. My code is as follows:

//Exponential Calculator (Power/Squre-Root/Cube-Root)
  const Exponents=()=>{
    const [result,setResult]=useState(null);
    const [choice,setChoice]=useState("Power");
    const [x,setX]=useState(null);
    const [n,setN]=useState(null);
    useEffect(()=>{

    },[choice,x,n,result]);
    const calcResult=()=>{
      let res=1;
      if(choice=="Power")
      {
        for(var i=1;i<=n;i  )
        res*=x;
      }
      else if(choice=="SquareRoot")
      res=Math.sqrt(x);
      else
      res=Math.cbrt(x);

      setResult(res);
    }
    const handleChange=(e)=>{
      reset();
      setChoice(e.target.value);
    }
    function reset(){
      setResult(null);
      setX(null);
      setN(null);
    }
    const choiceData=()=>{
      if(choice==="Power"){
        return {
          name:"Power",
          quantities:["Base","Exponent"],
          disabled:false
        }
      }
      else if(choice==="SquareRoot")
      {
        setN(0.50);
        return{
          name:"Square-Root",
          quantities:["Base","Exponent"],
          disabled:true
        }
      }
      else if(choice==="CubeRoot")
      { 
        setN(0.34);
        return{
          name:"Cube-Root",
          quantities:["Base","Exponent"],
          disabled:true
        }
      }
    }
    return(
      <>
      <Form>
      <Form.Group className="mb-4" controlId="choice">
            <Form.Label>Select the type of calculation</Form.Label>
            <Form.Control
              as="select"
              className="select-custom-res"
              onChange={(e) => handleChange(e)}
            > 
              <option value="Power">Power</option>
              <option value="SquareRoot">Square Root</option>
              <option value="CubeRoot">Cube Root</option>
            </Form.Control>
          </Form.Group>
          <Form.Group className="mb-4" controlId="text">
            <Form.Text className="text">
              <strong>
                To find the {choiceData().name}, Enter the following values
              </strong>
              <br />
            </Form.Text>
          </Form.Group>
          <Form.Group className="mb-4">
            <Form.Label>{choiceData().quantities[0]}</Form.Label>
            <Form.Control
              onChange={(e) => setX(e.target.value)}
              type="number"
              placeholder={"Enter the Base"}
              value={x === null ? "" : x}
            />
          </Form.Group>
          <Form.Group className="mb-4">
            <Form.Label>{choiceData().quantities[1]}</Form.Label>
            <Form.Control
              onChange={(e) => setN(e.target.value)}
              type="number"
              placeholder={"Enter the Exponent"}
              value={n === null ? "" : n}
              disabled={choiceData().disabled}
            />
          </Form.Group>
          <Form.Group className="mb-4">
            <Form.Control
              readOnly
              type="number"
              placeholder={result === null ? "Result" : result   " "}
            />
          </Form.Group>
      </Form>
        <div className="button-custom-grp">
          <Button variant="primary" onClick={calcResult}>
            Calculate
          </Button>
          &nbsp;&nbsp;&nbsp;
          <Button variant="dark" onClick={() => reset()} type="reset">
            Reset
          </Button>
        </div>
      </>
    )
  }

On changing the choice from Power to SquareRoot I get an error:Too many rerenders. Interestingly, when I remove the setState line in the choiceData function,the error vanishes. Although I have used useEffect to prevent re-renders, it's not working.

CodePudding user response:

Issue

You call choiceData in the render return, which should be free of side-effects, like enqueueing state updates. When you call setN in the choiceData function it triggers a rerender, which calls choiceData again, which triggers a rerender... repeat ad nauseam.

Solution

I suggest converting choiceData into a chunk of state, and use an useEffect hook to update it and the n state depending on the choice state. In the render return, instead of calling a function to get a value, i.e. choiceData().quantities[0], you instead just access a property of the new choiceData state, i.e. choiceData.quantities[0].

const Exponents = () => {
  const [result, setResult] = useState(null);
  const [choice, setChoice] = useState("Power");
  const [choiceData, setChoiceData] = useState({});
  const [x, setX] = useState(null);
  const [n, setN] = useState(null);

  useEffect(() => {
    if (choice === "Power") {
      setChoiceData({
        name: "Power",
        quantities: ["Base", "Exponent"],
        disabled: false
      });
    } else if (choice === "SquareRoot") {
      setN(0.5);
      setChoiceData({
        name: "Square-Root",
        quantities: ["Base", "Exponent"],
        disabled: true
      });
    } else if (choice === "CubeRoot") {
      setN(0.34);
      setChoiceData({
        name: "Cube-Root",
        quantities: ["Base", "Exponent"],
        disabled: true
      });
    }
  }, [choice]);

  useEffect(() => {
    // is this effect used for anything?
  }, [choice, x, n, result]);

  const calcResult = () => {
    let res = 1;
    if (choice == "Power") {
      for (var i = 1; i <= n; i  ) res *= x;
    } else if (choice == "SquareRoot") res = Math.sqrt(x);
    else res = Math.cbrt(x);

    setResult(res);
  };

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

  function reset() {
    setResult(null);
    setX(null);
    setN(null);
  }

  return (
    <>
      <Form>
        <Form.Group className="mb-4" controlId="choice">
          <Form.Label>Select the type of calculation</Form.Label>
          <Form.Control
            as="select"
            className="select-custom-res"
            onChange={(e) => handleChange(e)}
          >
            <option value="Power">Power</option>
            <option value="SquareRoot">Square Root</option>
            <option value="CubeRoot">Cube Root</option>
          </Form.Control>
        </Form.Group>
        <Form.Group className="mb-4" controlId="text">
          <Form.Text className="text">
            <strong>
              To find the {choiceData.name}, Enter the following values
            </strong>
            <br />
          </Form.Text>
        </Form.Group>
        <Form.Group className="mb-4">
          <Form.Label>{choiceData.quantities[0]}</Form.Label>
          <Form.Control
            onChange={(e) => setX(e.target.value)}
            type="number"
            placeholder={"Enter the Base"}
            value={x === null ? "" : x}
          />
        </Form.Group>
        <Form.Group className="mb-4">
          <Form.Label>{choiceData.quantities[1]}</Form.Label>
          <Form.Control
            onChange={(e) => setN(e.target.value)}
            type="number"
            placeholder={"Enter the Exponent"}
            value={n === null ? "" : n}
            disabled={choiceData.disabled}
          />
        </Form.Group>
        <Form.Group className="mb-4">
          <Form.Control
            readOnly
            type="number"
            placeholder={result === null ? "Result" : result   " "}
          />
        </Form.Group>
      </Form>
      <div className="button-custom-grp">
        <Button variant="primary" onClick={calcResult}>
          Calculate
        </Button>
        &nbsp;&nbsp;&nbsp;
        <Button variant="dark" onClick={() => reset()} type="reset">
          Reset
        </Button>
      </div>
    </>
  );
};
  • Related