"Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. at renderWithHooksAgain (react-dom-client.development.js:5614:17) at renderWithHooks (react-dom-client.development.js:5532:21) at updateFunctionComponent (react-dom-client.development.js:8897:19) at beginWork (react-dom-client.development.js:10522:18) at runWithFiberInDEV (react-dom-client.development.js:1519:30) at performUnitOfWork (react-dom-client.development.js:15132:22) at workLoopSync (react-dom-client.development.js:14956:41) at renderRootSync (react-dom-client.development.js:14936:11) at performWorkOnRoot (react-dom-client.development.js:14462:44) at performWorkOnRootViaSchedulerTask (react-dom-client.development.js:16216:7)"
"hook.js:608 An error occurred in the <App> component.
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://react.dev/link/error-boundaries to learn more about error boundaries."
This is my code in the app.jsx component:
const tipPercents = [5, 10, 15, 25, 50];
export default function App() {
const { register, handleSubmit, setValue, watch } = useForm();
const [tipPercent, setTipPercent] = useState(null);
const customTip = parseFloat(watch('customTip')) || null;
const bill = parseFloat(watch('bill')) || 0;
const people = parseFloat(watch('people')) || 0;
const tip = customTip || tipPercent || 0;
const tipAmount = people ? ((bill * tip) / 100) / people : 0;
const total = people ? (bill + (bill * tip) / 100) / people : 0;
const handleTipClick = (value) => {
setTipPercent(value);
setValue('customTip', '');
setValue('tipPercent', value)
};
const reset = () => {
setTipPercent(null);
setValue('bill', '');
setValue('people', '');
setValue('customTip', '');
};
return (
<div>
<div>
<form onSubmit={handleSubmit(() => {})}>
<div className='left'>
<div>
<label>Bill</label>
<div>
<img src={dollar} alt="bill" />
<input type="number" placeholder='0' {...register('bill')} />
</div>
</div>
<div>
<p>Select Tip %</p>
<div>
{/* tip button components go here */}
{tipPercents.map((percent) => (
<TipButton
key={percent}
value={percent}
isActive={tipPercent === percent}
onClick={handleTipClick}
/>
))}
</div>
<input
type="number"
placeholder='Custom'
{...register('customTip')}
onFocus={setTipPercent(null)}
/>
</div>
<div>
<label>Number of People</label>
<div>
<img src={person} alt="person" />
<input type="number" placeholder='0' {...register('people')} />
</div>
</div>
</div>
<div className='right'>
<div>
{/* result components go here */}
<ResultRow title={"Tip Amount"} amount={tipAmount.toFixed(2)} />
<ResultRow title={"Total"} amount={total.toFixed(2)} />
</div>
<button type='button' onClick={reset}>RESET</button>
</div>
</form>
</div>
</div>
)
}
this is tipButton.jsx component:
export default function TipButton ({ value, onClick, isActive }) {
return (
<button
type="button"
onClick={() => onClick(value)}
className={`tip-button ${isActive === value ? 'active-button' : ''}`}
>
{value}%
</button>
)
}
ResultRow.jsx:
export default function ResultRow ({ title, amount }) {
return (
<div>
<div>
<p>{title}</p>
<p>/ person</p>
</div>
<h3>${amount}</h3>
</div>
)
}
I asked chatgpt and it said the problem is related to something like setValue or onClick function for TipButton component. I tried the solution it gave me but it didn't work.
onFocus={setTipPercent(null)}
This triggers a state update during rendering, which forces React to re-render, which then calls setTipPercent(null) again, creating an infinite loop.
Try changing it to use an arrow function to delay execution until focus:
onFocus={() => setTipPercent(null)}