Update 22 July 2019: Project dependencies updated.

Babel 7.x

React 16.x

Redux 4.x

Webpack 4.x

Check the package.json for complete list of updates.

New dependencies

To write tests against our actions, reducers and operations, we will need to add jest as a dev dependency to our project. Jest is nice because it contains everything we need to write, run and gather coverage for our code.

We will also be using redux-thunk to allow us to conditionally dispatch actions for our game.

yarn add --dev jest redux-thunk

Update the package.json file’s test script with the jest cli command to execute all tests found in our project. Jest will default to looking for files that start with, “test, *.test, or *.spec.”

// package.json scripts: {

...,

...,

"test": "jest"

} // rest of package.json

Game structure

Tic tac toe is a pretty simple game. There is a grid made up of three by three squares where each player takes a turn marking an ‘X’ or an ‘O’ in each square. The first player to get three in a row, column or diagonal wins. Since the game is so simple, we only need four states.

board — 3x3 number array; the current state of the game board.

— 3x3 number array; the current state of the game board. gameover — boolean; there is a winner or it’s a draw.

— boolean; there is a winner or it’s a draw. player — number; the current player, or none (0).

— number; the current player, or none (0). winner — number; the player that won the game, or draw (0).

Managing state

We are going to be using the re-ducks pattern to organize our redux reducers, types and actions into ‘ducks’. There are many ways of managing and organizing your redux files, but I like the re-ducks pattern because it breaks the state out into folders that are easy to manage and make it easier for us to write tests.

A typical ‘duck’ will have the following files in it:

index.js — exports for our duck.

— exports for our duck. reducers.js — redux reducers.

— redux reducers. actions.js — actions that can be be used by our operations.

— actions that can be be used by our operations. operations.js — operations that can be performed on our duck. An operation can dispatch one or more actions.

— operations that can be performed on our duck. An operation can dispatch one or more actions. selectors.js — maps complex state or derive value assigned to props (optional).

— maps complex state or derive value assigned to props (optional). types.js — action type constants.

— action type constants. test.js — tests for our duck.

The ducks are stored under the folder: src/state/ducks/.

Game duck — Reducers, actions and types

The only duck for our game will be the ‘game’ duck. It will contain the four states listed in the, “Game structure,” section.

Create a game folder under src/state/ducks/ and add the files listed in the, “Managing state,” section to it.

We will start by writing tests for our reducers, types and actions. Open the test.js file and add this code to it:

If you run yarn test now, you will get failing tests with messages that say, “TypeError: reducers is not a function,” or “TypeError: action.* is not a function,” since we haven’t implemented them yet.

Open the reducers.js , actions.js and types.js file and add the following implementation:

game/reducers.js

game/actions.js

game/types.js

Run yarn test again and you should see that they are all passing now.

Game duck — operations and selectors

The re-ducks pattern describes the operations as the interface of our duck. Operations exposed through our duck can be used to update the state of our duck.

Some of our duck operations are simple and can be exposed one for one with their action, but there are others that are a little more complex and require some logic before they dispatch actions to change state.

We will first update our test.js file with a new section for the operations before implementing them in the operations.js and utils/game.js files.

game/test.js

game/operations.js

src/state/utils/game.js — note that the `utils` folder is a sibling of the ducks folder.

NOTE: We are not using Selectors, but they can be used to take our duck state and derive a new state from it. They should be pure functions, like reducers, that don’t change the state of our duck.

We can just have an empty export in the selectors.js file as a placeholder.

// ducks/game/selector.js export {};

Game duck — exposing the duck

Now that we have our duck implemented, we need to expose it so that the components can use it to render their state.

In the index.js file, export the reducers, operations, selectors and types:

ducks/game/index.js

Create another index.js file at src/state/ducks/ to group all of the individual ducks ( game only, for now). These are the reducers that will be combined in the root reducer of the redux store.

// src/state/ducks/index.js export { default as gameState } from './game';

// ...

// more exports from other ducks

Redux store

To use the game duck with redux, we need to add its reducers to the redux store. Create a store.js file at src/state/ to configure the store.

state/store.js

We export the configureStore function which configures the store by combining all of our ducks down into one root reducer, adding the thunk middleware to allow us to defer or conditionally dispatch actions and apply any initial state before creating and return the store.

Using the duck

Open the src/index.js file and update it to use the redux store we created.

src/index.js

We use the react-redux Provider to provide our store to all of our components.

At a later point, if we wanted to persist our state to local storage, we can use the initialState variable to load it again if we refresh or come back to the game after navigating away.

In the src/views/App.jsx file, update it to use our game duck.

src/views/App.jsx

Test to see if our app is still working by running yarn start .

To be continued…

All of the source code in this part can be found here: