I am a very beginner in react and redux and I am self studying.
States in my tic tac toe project, are handled by redux.
I wanted to declare the winner when X or O are aligned.
this is my code:
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import tactactoeReducer from '../components/ticTacToeSlice';
export const store = configureStore({
reducer: {
tactactoeReducer
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
// hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// ticTacToeSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "../app/store";
type Cell = 'X' | 'O' | '-'
export type Table = Array<Array<Cell>>
export type Turn = Exclude<Cell, '-'>
export interface State {
table: Table
turn: Turn
}
const initialState: State = {
table: [
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
],
turn: "X"
}
export interface TurnAction {
i: number,
j: number,
}
export const tactactoeSlice = createSlice({
name: "tactactoe",
initialState,
reducers: {
turn: (state, action: PayloadAction<TurnAction>) => {
state.table[action.payload.i][action.payload.j] = state.turn
state.turn = state.turn == 'O' ? 'X' : 'O'
}
},
});
export const { turn } = tactactoeSlice.actions;
export const turnSelector = (state: RootState) => state.tactactoeReducer;
export default tactactoeSlice.reducer;
ticTacToe.tsx
import { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import { State, turn, turnSelector } from "./ticTacToeSlice";
function TicTacToe() {
const [states, setStates] = useState<State>();
const selectedTurn = useAppSelector(turnSelector);
const dispatch = useAppDispatch();
useEffect(() => {
setStates(selectedTurn);
}, [selectedTurn]);
function handleTurn(i: number, j: number) {
const constTurn = {
i: i,
j: j
};
dispatch(turn(constTurn));
}
return (
<div className="container">
<h3>current turn: {states?.turn}</h3>
<table>
{
states?.table.map((row, index) => {
return (
<tr>
{row[0] === "-" ? (
<td className="paper-btn" onClick={() => { handleTurn(index, 0) }}>{row[0]}</td>
) : (
<td className="paper-btn">{row[0]}</td>
)}
{row[1] === "-" ? (
<td className="paper-btn" onClick={() => { handleTurn(index, 1) }}>{row[1]}</td>
) : (
<td className="paper-btn">{row[1]}</td>
)}
{row[2] === "-" ? (
<td className="paper-btn" onClick={() => { handleTurn(index, 2) }}>{row[2]}</td>
) : (
<td className="paper-btn">{row[2]}</td>
)}
</tr>
)
})
}
</table>
</div>
);
}
export default TicTacToe;
I checked react.dev and many similars documentations for a solution.
I get the part which the algorithm is comparing my values by index in horizontal, vertical and diagonal lines like blow:
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
but I don't know how to do that due to my code which contains redux as an state manager.
I checked react.dev and many similars documentations for a solution. I get the part which the algorithm is comparing my values by index in horizontal, vertical and diagonal lines like below:
but I don't know how to do that due to my code which contains redux as an state manager.
The React tutorial has already defined a function (function calculateWinner(squares)
) that takes an array of squares and returns the winner, if the game has been won. We can use that same function in a React and Redux app. Your question is about where we can use it.
I would put this is a selector function, since it involves deriving a value (the winner) from data which you selected from your Redux store (the current board).
You are storing your game state in a 2-dimensional 3x3 array, while the tutorial uses a 1D array of all 9 cells. This is not a problem as you can use .flat()
to convert your data from 2D to 1D, as required by the calculateWinner
function.
function calculateWinner(squares) {
// copy and paste from tutorial
}
// This selects the 2D array that you have in your redux state.
export const selectSquares = (state: RootState) => state.tactactoeReducer.table;
// This selects the current winner.
export const selectWinner = createSelector(
// First we select the raw data that we need from Redux.
selectSquares,
// Then we convert it to the output that we want.
(squares) => {
const squaresArray = squares.flat();
return calculateWinner(squaresArray);
}
);
Then use this selector function in your TicTacToe
component.
const winner = useAppSelector(selectWinner);
Note: you do not need the useState
or useEffect
in your TicTacToe
component. You can use the data directly from the useAppSelector
hook.