Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

problem in counting times that a function called in typescript

I’m a very beginner in react and redux and I’m working on a tic tac toe project using this two.
Recently I ran to a new problem which is my function count++ does not work correctly.
I’ll put the whole code in the end of the question, but this is a summary of the code which has the problem.

// my function which I want to count the times it's called by onClick event

let count = 0
  const handleTurn = (i: number, j: number) => {
    // this is the counter that doesn't work correctly
    count ++
    console.log(count)

    const newTurn = {
      i: i,
      j: j
    };
    const newGameStatus = {
      message: status
    }

    if (winner === null) {
      dispatch(turn(newTurn));
      dispatch(gameStatus(newGameStatus))
      
    }
  }
// buttons that are calling my function

<table>
        <tbody>
          {states?.table.map((row, index) => {
            return (
              <tr key={index}>
                {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>
            )
          })}
        </tbody>
      </table>

and the output will be like this (highlighted in red):
the wrong output

But when I deleted all the content of my function it worked correcly

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  let count = 0
  const handleTurn = (i: number, j: number) => {
    count ++
    console.log(count)
  }

and the output will be correct:
right output

I want to have all the content in my function but also the counter works correct, and I don’t what’s the problem with my function or code.

also my whole code (including the problem part):

// store.ts

import { configureStore } from '@reduxjs/toolkit';
import tictactoeReducer from '../components/ticTacToeSlice';
import storage from 'redux-persist/lib/storage';
import { persistReducer, persistStore } from 'redux-persist';
import thunk from 'redux-thunk';

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, tictactoeReducer)

export const store = configureStore({
  reducer: {
    persistedReducer,
  },
  devTools: process.env.NODE_ENV !== 'production',
  middleware: [thunk]
});

export const persistor = persistStore(store)
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 { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "../app/store";

type Cell = 'X' | 'O' | '-'
type Table = Array<Array<Cell>>
type Turn = Exclude<Cell, '-'>

export interface State {
  move: string[]
  table: Table
  turn: Turn
  gameStatus: string
}

const initialState: State = {
  move: [],
  table: [
    ['-', '-', '-'],
    ['-', '-', '-'],
    ['-', '-', '-']
  ],
  turn: "X",
  gameStatus: "choose a box to start"
}

interface TurnAction {
  i: number,
  j: number,
}

interface GameStatusAction {
  message: string
}

interface moveAction {
  index: string[]
}

export const tictactoeSlice = 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'
    },
    move: (state, action: PayloadAction<moveAction>) => {
      state.move = action.payload.index
    },
    gameStatus: (state, action: PayloadAction<GameStatusAction>) => {
      state.gameStatus = action.payload.message
    },
    reset: () => initialState
  },
});

function calculateWinner(squares: Cell[]) {
  const lines = [
  // a, b, c 
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],

    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],

    [0, 4, 8],
    [2, 4, 6]
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] !== '-' && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

export const selectSquares = (state: RootState) => state.persistedReducer.table

export const selectWinner = createSelector(
  selectSquares,
  (squares) => {
    const squaresArray = squares.flat();
    return calculateWinner(squaresArray);
  }
);

export const { turn, reset, move, gameStatus } = tictactoeSlice.actions;
export const turnSelector = (state: RootState) => state.persistedReducer;
export default tictactoeSlice.reducer;
// ticTacToe.tsx

import { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import { State, reset, selectWinner, gameStatus, turn, turnSelector } from "./ticTacToeSlice";

function TicTacToe() {

  const [states, setStates] = useState<State>();
  const selectedTurn = useAppSelector(turnSelector);
  const dispatch = useAppDispatch();
  const status = "game is on"
  

  useEffect(() => {
    setStates(selectedTurn);
  }, [selectedTurn]);

  let count = 0
  const handleTurn = (i: number, j: number) => {
    count ++
    console.log(count)
    const newTurn = {
      i: i,
      j: j
    };
    const newGameStatus = {
      message: status
    }

    if (winner === null) {
      dispatch(turn(newTurn));
      dispatch(gameStatus(newGameStatus))
      
    }
  }

  const winner = useAppSelector(selectWinner);

  return (
    <div className="container">
      <div>
        {winner === null ? (
          <div>

            {states?.turn === "X" ? (
              <h3>current turn: <span className="text-secondary">{states?.turn}</span></h3>
            ) : (
              <h3>current turn: <span className="text-danger">{states?.turn}</span></h3>
            )}

            <h3>{states?.gameStatus}</h3>

          </div>
        ) : (
          <h3>winner is <span className="text-success">{winner}</span></h3>
        )}
      </div>

      <table>
        <tbody>
          {states?.table.map((row, index) => {
            return (
              <tr key={index}>
                {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>
            )
          })}
        </tbody>
      </table>
      <br />

      <input type="button" className="paper-btn btn-primary-outline" value="Reset" onClick={() => dispatch(reset())} />

    </div>
  );
}

export default TicTacToe;
/* settings.css */

.disabled-btn {
    pointer-events: none;
}

(sorry if I had grammer mistakes)

>Solution :

Because on every render the component does this:

let count = 0

So count is declared and initialized to 0 every time. Contrast this with a value that is successfully persisting across multiple renders:

const [states, setStates] = useState<State>();

The difference is the use of state. To maintain the value of count across renders, put it in state:

const [count, setCount] = useState(0);

Then instead of directly incrementing count, update its state:

const handleTurn = (i: number, j: number) => {
  setCount(count + 1);
  console.log(count + 1);
  //...
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading