Building a Coin-Tossing Simulator with React, Hooks, and Vercel: Part 2
In the first part of this tutorial, we built a simple React app with a button to toss a coin and tell us which side it landed on.
(You can see the source code of where we left off here and run the app here)
In this second part, weāre going to start tracking the number of times each side appears, have the coin tossed automatically for us (rather than having to press a button) and visualise the results over time.
Counting Each Side
Currently, our app tracks the total number of times the coin has been tossed, but youāll remember that from the start, we wanted to know how many times each side has appeared. Letās change our code to show us that information.
In App.js
, weāll add a few new variables using useState
:
function App() {
const [side, setSide] = useState(1)
const [heads, setHeads] = useState(0)
const [tails, setTails] = useState(0)
const tossed = heads + tails
const tossCoin = () => {
const landedOn = Math.round(Math.random())
if (landedOn === 1) {
setHeads(heads + 1)
} else {
setTails(tails + 1)
}
setSide(landedOn)
}
return (
<div>
<p>The coin has been tossed {tossed} times.</p>
<p>It landed on {side === 1 ? "heads" : "tails"}</p>
<ul>
<li>Heads: {heads}</li>
<li>Tails: {tails}</li>
</ul>
<button onClick={tossCoin}>Toss coin</button>
</div>
)
}
Weāve added four new variables, heads
, setHeads
, tails
, and setTails
, to
get and set the number of times that heads and tails each appear.
Weāve removed the useState
function call that sets tossed
and setTossed
and instead weāll set const tossed = heads + tails
, just to reduce the number
of āsources of truthā for the total number of times the coin has been tossed.
Weāve also added a ul
element to the rendered output where we can see the
values of heads
and tails
, but just showing a number isnāt very interesting.
Visualising The Coin Tosses
Thankfully, thereās a native HTML element that is perfectly suited for showing
the number of times each side appears out of a total: the meter
element.
We use the meter
element like so:
<meter value="50" max="100" />
This would render a meter thatās filled halfway. Letās use this element to
display how many times each side has landed inside the ul
we added earlier:
<ul>
<li>
<label htmlFor="heads">Heads: {heads}</label>
<meter id="heads" value={heads} max={tossed} />
</li>
<li>
<label htmlFor="tails">Tails: {tails}</label>
<meter id="tails" value={tails} max={tossed} />
</li>
</ul>
Automatically Tossing The Coin
The final step in this part of the tutorial will be to have the coin tossed
automatically. To do this, weāll use another React hook: useEffect
.
The useEffect
hook is used to call a function whenever a component renders or
is re-rendered (which happens if its propertiesāpropsāor state changes). This is
a bit abstract, but itās useful to us: we want to call a function when our app
renders so that we can toss the coin automatically.
In App.js
, weāll get the useEffect
hook the same way we get the useState
hook:
const { useEffect, useState } = React
And weāll add a useEffect
call to our App component. We can add this right
before the return
statement:
...
useEffect(() => {
const interval = setInterval(tossCoin, 500)
return () => clearInterval(interval)
})
return (
<div>
...
This will cause the tossCoin
function to be called every 500 milliseconds, or
every Ā½ a second. Note that in the useEffect
call, we return a function that
clears the interval: this is basically to clean up after ourselves if the App
stops rendering, since we donāt then need to keep calling tossCoin
every
500ms, and doing so could actually lead to bugs or slowness.
You can change the interval to a different value to have the coin tossed more or less frequently, but itās already very useful: now that the coin is being tossed automatically, you can see within just a few seconds that the frequency of each side landing averages out to be about even quite quickly!
Letās make one more change. Now that the coin is tossed automatically, the ātoss coinā button is a bit useless. Letās update our app so that thereās a āpauseā button that lets us momentarily stop the automatic tossing and manually toss the coin again.
function App() {
const [side, setSide] = useState(1)
const [heads, setHeads] = useState(0)
const [tails, setTails] = useState(0)
const [isPaused, setIsPaused] = useState(false)
const tossed = heads + tails
const tossCoin = () => {
const landedOn = Math.round(Math.random())
if (landedOn === 1) {
setHeads(heads + 1)
} else {
setTails(tails + 1)
}
setSide(landedOn)
}
useEffect(() => {
const interval = setInterval(() => {
if (!isPaused) {
tossCoin()
}
}, 10)
return () => clearInterval(interval)
})
return (
<div>
<p>The coin has been tossed {tossed} times.</p>
<p>It landed on {side === 1 ? "heads" : "tails"}</p>
<ul>
<li>
<label htmlFor="heads">Heads: {heads}</label>
<meter id="heads" value={heads} max={tossed} />
</li>
<li>
<label htmlFor="tails">Tails: {tails}</label>
<meter id="tails" value={tails} max={tossed} />
</li>
</ul>
<button onClick={() => setIsPaused(!isPaused)}>
{!isPaused ? "Pause" : "Continue"}
</button>
{isPaused && <button onClick={tossCoin}>Toss coin</button>}
</div>
)
}
The complete app code is seen above, with the changes necessary to add the āpauseā button. Letās walk through them together.
Weāve added an isPaused
and setIsPaused
variable to determine whether the
app is running, and set it to false
by default.
Weāve changed the useEffect
callback a bit:
useEffect(() => {
const interval = setInterval(() => {
if (!isPaused) {
tossCoin()
}
}, 10)
return () => clearInterval(interval)
})
Now, we check if isPaused
is false
before calling the tossCoin
function.
Finally, in our rendered output, we add the new āPauseā button, which toggles
the isPaused
variable, and only show the ātoss coinā button if the isPaused
variable is false (since !true
is equal to false
):
(
<button onClick={() => setIsPaused(!isPaused)}>
{!isPaused ? "Pause" : "Continue"}
</button>
{ isPaused && <button onClick={tossCoin}>Toss coin</button> }
)
And thatās it! Now our app will automatically toss the coin for us, and show us a nice visualisation of the number of times each side is shown.
In the next and final part of this tutorial, weāll keep track of the patterns that emergeāthe number of times that each side shows consecutivelyāand weāll publish our app publicly using a service called Vercel.