Bootstrap

react之实例:井字棋游戏(直接引用react)

1.分析需求

这里写图片描述 演示链接地址

井字棋游戏,两位玩家,分别是‘O’ 和 ‘X’ 分别落子,先完成三点一线的获胜,也就是无论横、竖、斜连成一条直线

组件拆分

  1. 棋子组件,共九个棋子
  2. 棋盘组件,九个方格形成棋盘
  3. 游戏组件,包括棋盘、下个玩家,还有游戏记录(可以悔棋)

2.页面搭建

引入必要js以及css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>井字棋游戏第一步</title>
    <script src="react.js"></script>
    <script src="react-dom.js"></script>
    <script src="browser.min.js"></script>
    <style>
        body {
            font: 14px "Century Gothic", Futura, sans-serif;
            margin: 20px;
        }

        ol, ul {
            padding-left: 30px;
        }

        .board-row:after {
            clear: both;
            content: "";
            display: table;
        }

        .status {
            margin-bottom: 10px;
        }

        .square {
            background: #fff;
            border: 1px solid #999;
            float: left;
            font-size: 24px;
            font-weight: bold;
            line-height: 34px;
            height: 34px;
            margin-right: -1px;
            margin-top: -1px;
            padding: 0;
            text-align: center;
            width: 34px;
        }

        .square:focus {
            outline: none;
        }

        .kbd-navigation .square:focus {
            background: #ddd;
        }

        .game {
            display: flex;
            flex-direction: row;
        }

        .game-info {
            margin-left: 20px;
        }

    </style>
</head>
<body>
    <div id="root"></div>
</body>
<script type="text/babel">

</script>
</html>

接下来的所有代码都在script中进行

3.第一步:创建组件

class Square extends React.Component{ //棋子组件
        constructor(){
            super()
        }
        render(){
            return(
                <button className="square">{this.props.value}</button>
            )
        }
    }
    class Board extends React.Component{  //棋盘组件
        renderSquare(i){
            return(
                <Square value={i}/>
            )
        }
        render() {
            return (
                <div>
                    <div className="board-row">
                        {this.renderSquare(0)}
                        {this.renderSquare(1)}
                        {this.renderSquare(2)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(3)}
                        {this.renderSquare(4)}
                        {this.renderSquare(5)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(6)}
                        {this.renderSquare(7)}
                        {this.renderSquare(8)}
                    </div>
                </div>
            );
        }
    }
    class Game extends React.Component{ //游戏组件
        render(){
            return(
                <div className="game">
                    <div className="game-board">
                        <Board/>
                    </div>
                    <div className="game-info">
                        <div>Next Player:</div> {/*显示下一步玩家*/}
                        <ol></ol>   {/*显示历史记录*/}
                    </div>
                </div>
            )
        }
    }
    ReactDOM.render(
        <Game/>,
        document.getElementById('root')
    )

这一步创建了三个类组件
显示结果:这里写图片描述 参考链接

注:在render()内的JSX语法 注释要写成 {/*注释内容*/}

第一步只将所有内容展示出来,并未加入任何事件


4.第二步:添加棋子点击事件

class Square extends React.Component{ //棋子组件
        constructor(){
            super();
            this.state = {
                value:null
            }
        }
        render(){
            return(
                <button className="square" onClick={()=>this.setState({value:'X'})}>
                    {this.state.value}
                </button>
            )
        }
    }

修改Square组件,默认棋子是空值,点击添加‘X’,由于这个值是交互式的,所以把value放在state内

显示结果:这里写图片描述 参考链接


5.第三步:状态提升

将Square的state提到父组件Board里,以props传入Square,使其父子组件同步

    function Square(props) {//棋子组件
            return(
                    <button className="square" onClick={props.onClick}>{props.value}
                    </button>
        )
    }
    class Board extends React.Component{  //棋盘组件
        constructor(){
            super();
            this.state = {
                squares :Array(9).fill(null)
            };
        }

        handleClick(i){
            const squares = this.state.squares.slice();//将原数组复制一份,为了之后做悔棋
            squares[i] = 'X';
            this.setState({squares: squares});
        }
        renderSquare(i){
            return(
                <Square value={this.state.squares[i]} onClick={() =>this.handleClick(i)}/>  /*将value和事件以props形式传入子组件*/
            )
        }
        render() {
            return (
                <div>
                    <div className="board-row">
                        {this.renderSquare(0)}
                        {this.renderSquare(1)}
                        {this.renderSquare(2)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(3)}
                        {this.renderSquare(4)}
                        {this.renderSquare(5)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(6)}
                        {this.renderSquare(7)}
                        {this.renderSquare(8)}
                    </div>
                </div>
            );
        }
    }

当您要聚合来自多个子节点的数据 或 使两个子组件之间相互通信时,提升 state(状态) ,使其存储在父组件中。父组件可以通过 props(属性) 把 state(状态) 传递回子组件,以使子组件始终与父组件同步。
如果组件中只包含一个render方法,可以将其改写为函数式组件,传入props即可

参考链接


6.第四步:轮流下棋、宣布获胜者

在第三步中只有一名玩家‘X’,先在添加一名‘O’,在state添加 xIsNext属性,用来判断下一位玩家是不是‘X’,在script添加获胜判断函数,并在handleClick中调用

轮流下棋
class Board extends React.Component{  //棋盘组件
        constructor(){
            super();
            this.state = {
                squares :Array(9).fill(null),
                xIsNext: true,
            };
        }

        handleClick(i){
            const squares = this.state.squares.slice();//将原数组复制一份,为了之后做悔棋
            squares[i] = this.state.xIsNext?'X':'O';
            this.setState({
                squares: squares,
                xIsNext: !this.state.xIsNext,
            });
        }
        renderSquare(i){
            return(
                <Square value={this.state.squares[i]} onClick={() =>this.handleClick(i)}/>  /*将value和事件以props形式传入子组件*/
            )
        }
        render() {
            const status = 'Next player:' + (this.state.xIsNext?'X':'O');
            return (
                <div>
                    <div className="status">{status}</div>
                    <div className="board-row">
                        {this.renderSquare(0)}
                        {this.renderSquare(1)}
                        {this.renderSquare(2)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(3)}
                        {this.renderSquare(4)}
                        {this.renderSquare(5)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(6)}
                        {this.renderSquare(7)}
                        {this.renderSquare(8)}
                    </div>
                </div>
            );
        }
    }

宣布获胜者

class Board extends React.Component{  //棋盘组件
        constructor(){
            super();
            this.state = {
                squares :Array(9).fill(null),
                xIsNext: true,
            };
        }

        handleClick(i){

            const squares = this.state.squares.slice();//将原数组复制一份,为了之后做悔棋
            if (calculateWinner(squares) || squares[i]) {
                return;
            }
            squares[i] = this.state.xIsNext?'X':'O';
            this.setState({
                squares: squares,
                xIsNext: !this.state.xIsNext,
            });
        }
        renderSquare(i){
            return(
                <Square value={this.state.squares[i]} onClick={() =>this.handleClick(i)}/>  /*将value和事件以props形式传入子组件*/
            )
        }
        render() {
            const winner = calculateWinner(this.state.squares);
            let status;
            if (winner) {
                status = 'Winner: ' + winner;
            } else {
                status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
            }
            return (
                <div>
                    <div className="status">{status}</div>
                    <div className="board-row">
                        {this.renderSquare(0)}
                        {this.renderSquare(1)}
                        {this.renderSquare(2)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(3)}
                        {this.renderSquare(4)}
                        {this.renderSquare(5)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(6)}
                        {this.renderSquare(7)}
                        {this.renderSquare(8)}
                    </div>
                </div>
            );
        }
    }
    function calculateWinner(squares) {
        const lines = [
            [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;
    }

参考链接


7.第五步:存储历史记录

将Board中的state提升到Game,并将Board中的this.state改为this.props

class Board extends React.Component{  //棋盘组件

        renderSquare(i){
            return(
                <Square value={this.props.squares[i]} onClick={() =>this.props.onClick(i)}/>  /*将value和事件以props形式传入子组件*/
            )
        }
        render() {
            return (
                <div>
                    <div className="board-row">
                        {this.renderSquare(0)}
                        {this.renderSquare(1)}
                        {this.renderSquare(2)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(3)}
                        {this.renderSquare(4)}
                        {this.renderSquare(5)}
                    </div>
                    <div className="board-row">
                        {this.renderSquare(6)}
                        {this.renderSquare(7)}
                        {this.renderSquare(8)}
                    </div>
                </div>
            );
        }
    }

将state以及事件放进Game组件

class Game extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                history: [
                    {
                        squares: Array(9).fill(null)
                    }
                ],
                stepNumber: 0,
                xIsNext: true
            };
        }

        handleClick(i) {
            const history = this.state.history.slice(0, this.state.stepNumber + 1);
            const current = history[history.length - 1];
            const squares = current.squares.slice();
            if (calculateWinner(squares) || squares[i]) {
                return;
            }
            squares[i] = this.state.xIsNext ? "X" : "O";
            this.setState({
                history: history.concat([
                    {
                        squares: squares
                    }
                ]),
                stepNumber: history.length,
                xIsNext: !this.state.xIsNext
            });
        }

        jumpTo(step) {
            this.setState({
                stepNumber: step,
                xIsNext: (step % 2) === 0,

            });
            if(step == 0){
                this.setState({
                    history: [
                        {
                            squares: Array(9).fill(null)
                        }
                    ]
                });
            }

        }

        render() {
            const history = this.state.history;
            const current = history[this.state.stepNumber];
            const winner = calculateWinner(current.squares);

            const moves = this.state.history.map((step, move) => {
                const desc = move ?
                    'Go to move #' + move :
                    'restart';
                return (
                    <li key={move}>
                        <button onClick={() => this.jumpTo(move)}>{desc}</button>
                    </li>
                );
            });

            let status;
            if (winner) {
                status = "Winner: " + winner;
            } else {
                status = "Next player: " + (this.state.xIsNext ? "X" : "O");
            }

            return (
                <div className="game">
                    <div className="game-board">
                        <Board
                            squares={current.squares}
                            onClick={i => this.handleClick(i)}
                        />
                    </div>
                    <div className="game-info">
                        <div>{status}</div>
                        <ol>{moves}</ol>
                    </div>
                </div>
            );
        }
    }
;