Highlighting possible moves
Overview
In the previous section, we were able to drag and drop pieces to make moves. In this section, we will focus on improving our players' experience by highlighting some of their valid cell options (candidate cells) once they start to drag a piece.
So far, we have been working with useState to manage state/data in our Game component and then passing down that data to other components such as Board and Cell through props. Whenever we update the state, e.g by calling setFen, our Game component is updated and so are our our other components since they receive new props.
Context API
To highlight valid cells for a move, we can follow the same approach by creating a new variable to hold the valid moves i.e const [validMoves, setValidMoves] = useState([]) and then pass down the validMoves value via props to the Board then to the Cell component that needs to be updated.
While this approach will work, as we add more features and state values to our game, we will also have to pass in more props down to our components. To avoid this, we will use another state management solution by React called the Context API. It allows us to share state values between components without having to pass them around through props. This will help us manage more values in state such as candidate cells, game over status, captured pieces and more, without passing in more props to our Board, Cell and Piece components.
To get started create a new folder in src and name it context. In the context folder, create a new file GameContext.js.
In GameContext.js, add the following
We create a new context object using createContext(), and provide an initial state value, which is our object with possibleMoves: []. Next, we create a GameProvider component, it takes a special prop called children. The children prop refers to the child nodes or content passed to this component within its tags. See the short example below
Next we use the useReducer hook. It's an alternative to useState. It makes it easier to manage and update complex or deeply nested state values. It takes in a reducer function, which is a function that updates the state, and the initial state value, initialState. It also returns an array with two values. The first value is the state and the second value is a function called dispatch that is used to trigger state updates. Whenever we call dispatch, the reducer function we passed in i.e GameReducer is going to be called to update the state.
Finally, we return the children wrapped in GameContext.Provider. GameContext.Provider takes a value prop where we pass the values we want to share with all the components in this tree.
For the value prop, we provide an object with all the properties in our state, which in this case is only the possibleMoves array, we also provide dispatch so that we can easily trigger state updates from nested children components.
That's it for the context set up. Let's create our GameReducer function in src/context/GameReducer.js. Create the GameReducer.js file in the context folder and add the following code.
Reducers
A reducer is a function that updates our state. It takes in the previous state value as its first argument and an action as its second, and based on the type of this action, it returns a new updated state.
This actions are usually dispatched from our components as we will see in a moment.
We use a switch statement to check the action type and handle different cases. If the type of action dispatched is types.SET_POSSIBLE_MOVES we return a new object and update the possibleMoves property to what is returned from getPositions(action.moves). When this action is dispatched from our Game component, the reducer will receive a property from the action called moves, it will be an array of the possible moves that need to be set e.g [a3, a4]. The items in the array are the cells that a piece can move to e.g a3, however some items can also include the piece as well as the cell e.g ['Na3', 'Nc3', 'Nf3', 'Nh3']. That's why we use the getPositions function to extract the cells and pass this value to our possibleMoves property.
When the type of action dispatched is types.CLEAR_POSSIBLE_MOVES, we update our state by returning a new object and setting the possibleMoves property to []. We will dispatch this action after the player finally makes a move and we now need to unhighlight any highlighted cells.
Actions
Let's create a file for this actions in src/context/actions.js. Actions are just strings that express various events/intents in our app. We define them as variables so that they can be easy to reuse and also to avoid mistyping them.
Let's add the following actions for now
useContext
Now let's make use of the context values in our components. In order to share context values between components, we need to wrap the components in a Provider component, in our case GameContext.Provider.
So let's wrap the main App component in src/App.js using the GameContext.Provider component we created earlier. This makes the values we passed in the value prop of the Provider component accessible by its child components as we shall see in a moment.
In the Game component we need to make the following additions. The rest of the code remains unchanged, only add the new parts :)
First, we import the useContext hook from React. useContext takes in a context value created using React.createContext like our GameContext.
useContext gives us access to the state values we provided in our context provider i.e GameContext.Provider such as dispatch.
In the setFromPos, which is called once a Piece is dragged (onDragStart), we make use of the dispatch function to dispatch an event of type
types.SET_POSSIBLE_MOVES, we also provide a moves property whose value we get from calling
chess.moves({ square: pos }) which returns a list of possible moves. Our GameReducer is called and it updates our setPossibleMoves array with the action.moves dispatched. Any the object we provide to dispatch({}) is received by our reducer as the value of action
In the makeMove function, we dispatch an action of type types.CLEAR_POSSIBLE_MOVES
Our reducer function is called and it receives this action and updates our state by setting possibleMoves: []. This will unhighlight any cells that had been highlighted.
To highlight our cells based on whether they are candidate moves, we need to get the current array of possibleMoves from our state. We can do that in the Cell component by using the useContext hook and providing our GameContext.
We get the possibleMoves from state and then check if this Cell's position is a possible move by
possibleMoves.includes(cell.pos)
In our return statement, we wrap our Piece in a div with a className overlay, we also apply the className possible-move conditionally if this cell is a possible move. That className adds a linear gradient to our cell to highlight it. See the modified css below
With that in place, we now get some highlighted cells to show us the valid moves we can make whenever we drag a piece. (Candidate cells)

Get the completed source code for this lesson here