I'm making a blackjack hand simulation and I've encountered an issue with my code. The game goes like this: users gets two random cards and a total of the points, clicks 'hit' to get another random card from the deck. Now that's all working but there's one more rule: if that card is an Ace, user chooses if they want to get 1 or 10 points. I implemented it before when I only had one card at a time with useEffect, however now I refactored my code and the total isn't kept in useState + the array has two cards that need to evaluated, not the most recent one.
I've tried putting my loop and if statement in a useEffect and conditionally render the Popup to let the user decide (with and without dependencies), but when I put the useState() to trigger the condition, it throws an error that there have been too many renders and I'm not sure why that is.
Here's my Home component:
import {useState, useEffect} from 'react'
import Card from '../components/Card';
import Total from '../components/Total';
import Popup from '../components/Popup'
import {shuffle} from '../hooks/shuffleCards'
import {deckArray} from '../utils/data'
export default function Home(){
const startHandSize = 2
const [starterDeck, setStarterDeck] = useState(shuffle(deckArray))
const [howManyDealt, setHowManyDealt] = useState(startHandSize)
const [triggerPopup, setButtonPopup] = useState(false)
const deal = () => {
setHowManyDealt(startHandSize)
setStarterDeck(shuffle(deckArray))
}
const hit = () => !bust && setHowManyDealt(prev => prev + 1)
const usersCards = starterDeck.slice(-howManyDealt)
var total = 0
usersCards.forEach(function (arrayItem) {
if(arrayItem.card === "A"){
alert("you have an ace")
}
else{
total += arrayItem.value
}
});
const bust = total > 21;
return(
<div>
<button onClick={deal}>DEAL</button>
<button disabled={bust} onClick={hit}>HIT</button>
<button disabled={bust}>STAND</button>
<Total total={total} usersCards={usersCards}/>
<Card usersCards={usersCards}/>
{triggerPopup && <Popup total={total} setButtonPopup={setButtonPopup}/>}
</div>
)
}
and my Popup:
export default function Popup({total, setButtonPopup}){
const handleClick = (points) => {
total += points
setButtonPopup(false)
}
return(
<div className="popup">
<div className="popup-inner">
<h4>You've got an Ace. Would you like to collect 1 or 10 points?</h4>
<button className=".btn-popup" onClick={() => handleClick(1)}>1 POINT</button>
<button className=".btn-popup" onClick={() => handleClick(10)}>10 POINTS</button>
</div>
</div>
)
}
Any help much appreciated!
Good attempt. However, there seems to be a general misunderstanding about state. Consider this code:
totalis a purely local variable toPopup, so this+=pretty much does nothing. To change state in the caller, you'd normally pass a callback that can trigger asetStateand move the new value fortotalinto state.Remember: any data change must happen immutably, and if you want to trigger a re-render, you have to set state. Of course, there are ways to circumvent this flow using refs and so forth, but these are escape hatches you shouldn't use if you don't have to.
However, a design with
totalkept in state strikes me as redundant. We already know the total based on the cards in play. A better strategy seems to be having ace values individually settable via the popup modal, assuming you don't want to auto-compute these ace values to be as high as possible without busting or use a toggle switch instead of a modal.I kept going with my code from your previous question and added the modal. I'm treating high aces as 11 per the rules of Blackjack, but you can easily make that 10 if you want.
As before, I'm hoping you can apply the techniques here to your code. The keys are the
handleAceSetcallback and the new piece of stateaceToSet, which is a ace the user has picked, or null if the user hasn't chosen an ace.aceToSetis like yoursetButtonPopup, but tracks an object or null rather than a boolean. WhenaceToSetisn't null, the user has selected an ace and we show the modal to let them pick a value for it.handleAceSetmay seem a bit complex, but it has to be due to immutability. It finds the index of the ace the user wants to set in the deck array, then creates a new object at this index with the new value and glues the subarray slices before and after the index back together.