我是react和redux的初学者,我正在用这两个工具做一个井字游戏。
最近我遇到了一个新问题,那就是我的函数count++不能正常工作。
我会把整个代码放在问题的最后,但这是有问题的代码的摘要。
// 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))
}
}
个字符
输出将是这样的(以红色突出显示):
的数据
但是当我删除了所有的内容,我的功能,它的工作正确
let count = 0
const handleTurn = (i: number, j: number) => {
count ++
console.log(count)
}
型
并且输出将是正确的:
的
我希望我的函数中的所有内容都正确,而且计数器也正确工作,我不知道我的函数或代码有什么问题。
我的整个代码(包括问题部分):
// 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;
}
(如果有语法错误,请见谅)
1条答案
按热度按时间okxuctiv1#
因为 * 在每次渲染 * 时,组件都会执行以下操作:
字符串
因此,每次都声明
count
并将其初始化为0
。将此值与在多个渲染中成功持久化的值进行对比:型
不同之处在于 state 的使用。若要在渲染之间保持
count
的值,请将其置于以下状态:型
然后,更新其状态,而不是直接递增
count
:型