简体   繁体   English

为什么使用Redux无法正确更改状态?

[英]Why won't my state change correctly using Redux?

I made a tic tac toe game, user vs computer. 我做了一个井字游戏,用户与计算机。 It works perfectly fine without the use of Redux, but ever since incorporating Redux into it, something's off. 它在没有使用Redux的情况下也可以正常工作,但是自从将Redux集成到其中以来,情况就不那么理想了。

What I'm trying to achieve is: If there's an empty square, then the CPU randomly place an O at an empty location (after the user places an X ) but this time using Redux. 我要实现的目标是:如果有一个空的正方形,则CPU将O随机放置在一个空位置(在用户放置X ),但这一次使用Redux。

I feel that let turnState = this.props.turnValueReducerRedux(this.state.turn); 我觉得let turnState = this.props.turnValueReducerRedux(this.state.turn); is what's causing the problem and/or my turnReducer.js file is incorrect. 是引起问题的原因和/或我的turnReducer.js文件不正确。

I've labled // START HERE and // END HERE to indicate where I think the problem possibly stems from. 我已经标记了// START HERE// END HERE以指出我认为问题可能出在哪里。

What am I doing wrong and how can I fix this? 我在做什么错,我该如何解决?

Here's Board.js 这是Board.js

import React, { Component } from 'react';
import './Board.css';
import { connect } from 'react-redux';
import * as actionTypes from '../../store/actions/actions';

class Board extends Component {
    constructor(props) {
        super(props);
        this.state = {
            winner: undefined,
        };

        this.gameState = {
            turn: 'X',
            gameLocked: false,
            gameEnded: false,
            board: Array(9).fill(''),
            totalMoves: 0
        }

        this.clicked = this.clicked.bind(this);
    }


    clicked(box) {
        if(this.gameState.gameEnded || this.gameState.gameLocked) {
            return;
        }

        // START HERE 
        let turnState = this.props.turnValueReducerRedux(this.gameState.turn);

        if(this.gameState.board[box.dataset.square] === '') {
            this.gameState.board[box.dataset.square] = turnState;
            box.innerText = this.gameState.turn;

            this.gameState.turnState = this.gameState.turnState === 'X' ? 'O' : 'X';
            this.gameState.totalMoves++;
        }
        // END HERE

        console.log("this.gameState.totalMoves ==> " + this.gameState.totalMoves);

        var result = this.checkWinner();

        if(result === 'X') {
            this.gameState.gameEnded = true;
            this.setState({
                winner: 'X',
                winnerLine: 'X wins'
            });
            console.log("X wins");
        } else if(result === 'O') {
            this.gameState.gameEnded = true;
            this.setState({
                winner: 'O',
                winnerLine: 'O wins'
            });
            console.log("O wins");
        } else if(result === "draw") {
            this.gameState.gameEnded = true;
            this.setState({
               winner: 'draw',
               winnerLine: 'match is a draw'
            });
        }
        console.log("result ==> " + result);

        if(this.gameState.turnState === 'O' && !this.gameState.gameEnded) {
            this.gameState.gameLocked = true;

            setTimeout(() => {
                do {
                    var random = Math.floor(Math.random() * 9);
                } while(this.gameState.board[random] !== '');

                this.gameState.gameLocked = false;
                console.log("reached here");
                this.clicked(document.querySelectorAll('.square')[random]);
            }, 3000)
        }
    }

    render() {
        return(
            <div id="game">
                <div id="state">{this.state.winnerLine}</div>
                <div id="head">
                    Tic Tac Toe
                </div>

                <div id="board" onClick={(e) => this.clicked(e.target)}>
                    <div className="square" data-square="0"></div>
                    <div className="square" data-square="1"></div>
                    <div className="square" data-square="2"></div>
                    <div className="square" data-square="3"></div>
                    <div className="square" data-square="4"></div>
                    <div className="square" data-square="5"></div>
                    <div className="square" data-square="6"></div>
                    <div className="square" data-square="7"></div>
                    <div className="square" data-square="8"></div>
                </div>
            </div>
        );
    }
}

const mapStateToProps = state => {
    return {
        turnValue: state.turnValue
    };

};

const mapDispatchToProps = dispatch => {
    return {
        turnValueReducerRedux: (value) => dispatch({type: actionTypes.TURN_VALUE, value})
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(Board);

Here's turnReducer.js : 这是turnReducer.js

import * as actionTypes from '../actions/actions';

const initialValue = {
    turnValue: 'O'
};

const turnReducer = (state = initialValue, action) => {
    switch (action.type) {
        case actionTypes.TURN_VALUE:
            console.log("turnReducer ==> " + action.value);
            return {
                ...state,
                turnValue: action.value
            };
        default:
            return state;
    }
}

export default turnReducer;

I think the connected dispatch action ( turnValueReducerRedux ) will not return what you're expecting. 我认为连接的分派操作( turnValueReducerRedux )将不会返回您期望的结果。 It would return the full action ({ type: '...', value: ... }) , not just the value, which would mean turnState would be incorrect in the following code: 它会返回完整的动作({ type: '...', value: ... }) ,而不仅仅是返回值,这意味着turnState在以下代码中将是错误的:

let turnState = this.props.turnValueReducerRedux(this.state.turn);

if(this.gameState.board[box.dataset.square] === '') {
  this.gameState.board[box.dataset.square] = turnState;

As a general recommendation, it seems you're not relying on state or the store for rendering the data, which one of Reacts main responsibilities. 作为一般建议,似乎您不依赖state或存储来呈现数据,这是React的主要职责之一。 So you are setting the value in the store by dispatching the action, but never accessing it with this.state.turnValue , which is defined by the following code: 因此,您将通过分派操作来在存储中设置值,但不要使用this.state.turnValue对其进行this.state.turnValue ,该代码由以下代码定义:

const mapStateToProps = state => {
    return {
        turnValue: state.turnValue
    };
};

Also be aware that gameState and state are very different, as the latter will trigger re-renders and be connected to the store, while the former will not. 另请注意, gameStatestate差异很大,因为后者将触发重新渲染并连接到商店,而前者则不会。


UPDATE UPDATE


As per our discussion below, here's an example that takes a different approach: https://codesandbox.io/s/x2x773pqxz 按照下面的讨论,这是一个采用不同方法的示例: https : //codesandbox.io/s/x2x773pqxz

The automatic computer player is the complicated part as this is a side effect of an action by the human player. 自动计算机播放器是复杂的部分,因为这是人类播放器的动作的副作用。 I therefore added redux-thunk to allow for certain actions to trigger other actions. 因此,我添加了redux-thunk以允许某些操作触发其他操作。 I've put a delay on the computers turn so you can see it in action. 我推迟了计算机的转动,以便您可以看到它的运行情况。

I've also split the Board and individual Squares into separate components, both of which are purely for presentation. 我还将董事会和各个Square拆分为单独的组件,这两个组件仅用于演示。 The Game component and the redux/actions.js files are the most important. Game组件和redux/actions.js文件是最重要的。

In theory you could choose whatever board size and number of players you like, but the css is just set up for a 3x3 board. 从理论上讲,您可以选择自己喜欢的棋盘大小和玩家人数,但是CSS只是为3x3棋盘设置的。

I didn't implement the findWinner logic because it felt like too much effort 😅 我没有实现findWinner逻辑,因为这感觉太费力了😅

Let me know if I can clarify anything, hope it helps! 让我知道是否可以澄清任何事情,希望对您有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM