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>
<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>
<Button variant="dark" onClick={() => reset()} type="reset">
Reset
</Button>
</div>
</>
);
};