Reputation: 153
I'm building a game in React Redux, and I have a condition where, upon being dealt cards, if the cards are over a certain amount I want a modal to put up so that the user can select one to discard.
Im doing this in the game logic by switching a part of the state called "cardHandOverflow" to true, and i want the modal to render on this condition. Im using React Modal library for this.
However im getting the error
react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
Here is the code im using:
export const CardHand: React.FC = () => {
const cardHandOverflow = useSelector((state: RootState) => state.gameStateReducer.players.filter(player => player.id === Player.id)[0].cardHandOverflow);
const [modalIsOpen, setIsOpen] = useState(false);
const closeModal = () => {
setIsOpen(false)
}
if (cardHandOverflow) {
setIsOpen(true)
}
return (
<>
{modalIsOpen ?
<DiscardModal
modalIsOpen={modalIsOpen}
closeModal={closeModal}
discardableCards={cards}
/> : null}
</>
)
Its obviously creating some sort of render loop with the useSelector and state change retriggering, however when i switch it out for a button the modal renders fine. How can i get the state change to render the modal once (so its acting like a click event)?
Many thanks :-)
Upvotes: 0
Views: 851
Reputation: 2379
This is caused by setIsOpen(true);
when cardHandOverflow
is truthy.
setIsOpen
causes a rerender, even when the value does not change, and since it is directly part of your function component logic it will be run ever render while cardHandOverflow
is true.
To avoid these loops you should make use of hooks provided by React, in this case useEffect
is most appropriate.
export const CardHand: React.FC = () => {
const cardHandOverflow = useSelector((state: RootState) => state.gameStateReducer.players.filter(player => player.id === Player.id)[0].cardHandOverflow);
const [modalIsOpen, setIsOpen] = useState(false);
const closeModal = () => {
setIsOpen(false)
}
useEffect(() => {
if (cardHandOverflow) {
setIsOpen(true)
}
}, [cardHandOverflow, setIsOpen])
return (
<>
{modalIsOpen ?
<DiscardModal
modalIsOpen={modalIsOpen}
closeModal={closeModal}
discardableCards={cards}
/> : null}
</>
)
}
The useEffect
will only run the contained code on component mount and whenever a value in the dependency array [cardHandOverflow, setIsOpen]
changes.
Upvotes: 1