How to make Life with React Hooks
The developer’s task is to show the user how digital cells live and die. The author took advantage of React and its hooks: state management and the ability to abstract from state-related logic make the project easy to read and understand. We share the implementation details and code on Github while we start Frontend development course…
Rules of the game
The universe of the game is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states: alive or dead (or inhabited and unpopulated, respectively). Each cell interacts with its eight neighbors – cells that are located side by side horizontally, vertically or diagonally. At each step in time, the following transitions occur:
Any living cell with fewer than two living neighbors dies of overpopulation.
Any living cell with two or three living neighbors is passed on to the next generation.
Any living cell with more than three living neighbors dies as if overpopulated.
Any dead cell with three living neighbors becomes a living cell, as if reproducing.
Try it my app, and then let’s talk about how it works under the hood.
The data structure I decided to use to represent the cells is pretty simple. Here is an array of objects:
We create a Grid display component, it is superimposed on the grid array and generates an individual cell for each object in the grid array:
It is seen that and div
Grid is a grid container, and the cell divs have inline styles, see lines 9 and 17. The style value is given by a helper function. The reason for this approach is that it allows you to dynamically change the grid and cell styles based on the data passed to the function.
gridSize
stores the size of the grid. I have three default sizes: 15×15, 30×30, or 50×50. Different sizes will have different styles. Let’s take a look at the helper functions:
Now let’s look at the logic for changing the cell depending on the generation and how the game controls are connected. All the logic related to state, as well as how we manage the appearance of the grid in a particular generation, is handled in a custom hook. useGrid
…
useGrid
contains multiple calls useState
to track the information we use to both iterate generations and control the game:
First, you need to find out if the grid has a combination of cells that you can change. The corresponding logic is located in a helper function stepThroughAutomata
inside useGrid
… I started to draw up a plan for the function using George Poya’s problem solving methods.
We bring the plan to life:
And it’s all! Let’s go to management.
So the first button here is Step 1 Generation.
Implementing the button is pretty straightforward: we have a function stepThroughAutomata
… And below we see the component Controls
…
On line 13, we have the first button. We just add the property onClick
to this button and pass to it stepThroughAutomata
:
On line 22, we have an input field that defines the iteration rate.
And finally, there is a third button, the value of which is “Start” or “Stop” depending on whether individual cells are clickable. If the cells are clickable, then the game is running. If not, the game is not running.
You might ask, “Wait a second, when I press the start button, the function stepThroughAutomata
does not start? ” Yes! JS method setInterval
doesn’t work very well with onClick
… Therefore, for this functionality, you had to create your own hook. Let’s see how it works:
Above, we are destructuring all data from useGrid
, but right below this code, we call another custom hook – useInterval
with four parameters. It:
Callback function (here –
stepThroughAutomata
).The time between calls to the passed function, in milliseconds. Meaning
speedInput
the default is 500.Current grid.
Boolean value, here
clickable
…
We created a hook useInterval
because the built-in setInterval function doesn’t always go well with how React re-renders components.
We need a way to know that the mesh is changing, and therefore the mesh needs to be re-rendered, and we need to make sure that it changes consistently every n
milliseconds. We can find out with the help of the built-in hook useRef
… First we initialize savedCallback
as a link.
Now let’s use useEffect
to set the current savedCallback
as the passed callback. It has to do with how setInterval
subscribes to an object window
and unsubscribes from him.
We will update savedCallback.current
every time the return value of the callback changes. This should happen every time the callback function is executed.
Moving on to the second challenge useEffect
… Let’s first check if it is true clickable
… If so, then you don’t want to run the function inside: such a launch means that the game is running right now. If clickable
– false, this means that the game is being launched for the first time. Therefore, we quickly initialize the function tick
which calls the currently stored callback.
Save the call result setInterval
passing tick
and delay, and then immediately unsubscribe using an anonymous function and doing clearInterval with passing id
…
The great thing is that the passed callback is the same function we use to iterate over one generation at a time, so the iteration algorithm is completely reusable.
Summary:
Hooks allow you to write clean code that can be reused.
Defining neighbors by flattening the mesh into a vector and performing mathematical operations to find neighbors gives spatial complexity
O(n)
…React’s built-in re-render function allows you to create a seamless UI representation of a Life game.
You can continue learning ReactJS in our courses:
Other professions and courses
Data Science and Machine Learning
Python, web development
Mobile development
Java and C #
From the basics to the depth
And: