React 文档 井字格小游戏 实现

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

// 获胜判定
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 {winner: squares[a],coordinate:[a,b,c]};
    }
  }
  return null;
}
// 渲染格子
function Square(props) {
  const {value,style} = props;
  return (
    <button 
      style={style}
      className="square"
      onClick={props.onClick}
    >
      {value}
    </button>
  );
}
// 渲染大棋盘,格子父组件,承担props
class Board extends React.Component {
  renderSquare(i) {
    const { squares, winnerList } = this.props;
    const style = winnerList.includes(i) ? {backgroundColor: 'beige'} : {};
    return <Square 
              key={i}
              style={style}
              value = {squares[i]}
              onClick={() => {this.props.onClick(i)}}
            />;
  }
  renderBoard(row = 3,column = 3) {
    return (
      <div>{
        Array(row).fill(null).map((itemM,m) => {
          return (
           <div className="board-row" key={m}>
            {
              Array(column).fill(null).map((itemN,n) => {
                return this.renderSquare(m * 3 + n)
              })
            }
           </div>
          )
        })
      }</div>
    )
  }

  render() {
    return this.renderBoard();
  }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: [],// 当前棋局布局
      squaresList: [],// 记录每一步棋局布局
      squareList: [],// 记录点击顺序,用于显示每一步坐标
      winnerList: [],
      xIsNext: true,
      isAsc: true //是否升序
    }
    this.renderLi = this.renderLi.bind(this);
    this.onClickButton = this.onClickButton.bind(this);
    this.clickSquare = this.clickSquare.bind(this);
    this.restartGame = this.restartGame.bind(this);
    this.desc = this.desc.bind(this);
  }
  //desc
  desc() {
    const { isAsc } = this.state;
    this.setState({
      isAsc: !isAsc
    })
  }
  // 重启游戏
  restartGame() {
    this.setState({
      squares: [],
      squareList: [],// 记录点击顺序
      squaresList: [],
      winnerList: [],
      xIsNext:true
    })
  }
  // 判断行列坐标
  checkCoordinate(index) {
    const x = Math.floor(index % 3);
    const y = Math.floor(index / 3);
    return {x,y}
  }
  // 点击列表按钮,跳转某一步
  onClickButton(index) {
    // 最后一步点击不再跳转
    if(index + 1 == this.state.squaresList.length) return;

    const squaresList = this.state.squaresList.slice(0, index + 1);
    const length = squaresList.length;

    this.setState({
      squares: squaresList[index],
      squaresList,
      winnerList: [],
      xIsNext: length % 2 === 0 // 奇数为true
    })
  }
  // 渲染右侧li列表
  renderLi() {
    const { squaresList, squareList, isAsc } = this.state;
    const length = squaresList.length - 1;
    return (
      squaresList.map((currentSquare,index) => {
        const {x,y} = this.checkCoordinate(squareList[index]);
        const status = `第${index+1}步`
        const coordinate = `坐标:(x: ${x+1} y: ${y+1})`
        const style = length === index ? {fontWeight: 'bold'} : {};

        return (
          <li key={index}>
            <button 
              style = {style}
              onClick={() => {this.onClickButton(index)}}
            >
              {status} {coordinate}
            </button>
          </li>
        )
      })
    )
  }
  // 点击格子
  clickSquare(index) {
    const { xIsNext, squaresList, squareList } = this.state;
    const squares = this.state.squares.slice();
    const winner = calculateWinner(squares);
    // 若获胜/格子存在 return
    if (winner || squares[index]) return;
    
    // 否则 1 更新squares 2 把squares加入squaresList 
    squares[index] = xIsNext ? 'X' : 'O';

    this.setState({
      squaresList: squaresList.concat([squares]),
      squareList: squareList.concat([index]),
      squares,
      xIsNext: !xIsNext
    },() => {
      // 在回调中判断是否获胜,并添加获胜样式
      const winner = calculateWinner(this.state.squares);
      if (winner) {
        this.setState({
          winnerList: [...winner.coordinate] 
        })
      }
    });
  }



  render() {
    const { squares, isAsc, winnerList, squaresList, xIsNext } = this.state;
    const winner = calculateWinner(squares);

    let status;
    if (winner) {
      status = `winner is ${winner.winner}`
    } else if(squaresList.length == 9) {
      status = `no winner`
    } else{
      status = `Next player: ${xIsNext ? 'X' : 'O'}`;
    }

    const ascButton = isAsc ? '降序' : '升序';
    let liList = this.renderLi();
    liList = isAsc ? liList : liList.reverse();
    
    return (
      <div className="game">
        <div className="game-board">
          <Board 
            squares={squares}
            onClick={this.clickSquare}
            winnerList={winnerList}
          />
        </div>
        <div className="game-info">
          <button onClick={this.desc}>{ascButton}</button>
          <div>{ status }</div>
          <button onClick={this.restartGame}>go to start</button>
          <ol>{liList}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容