让我们用 React 创建一个井字游戏。

2025-06-04

让我们用 React 创建一个井字游戏。

欢迎大家,
在本文中,我们将使用 react 和 react hooks 创建一个井字游戏应用程序。

在我们开始创建应用程序之前,您应该熟悉
Javscript、React 和 React-hooks。

所以,不要浪费时间,让我们开始项目设置。

项目设置

在终端中转到您想要创建 react-app 的目录,
然后运行以下命令。

npx create-react-app tic-tac-toe 
Enter fullscreen mode Exit fullscreen mode

为了创建我们的应用程序,我使用了create-react-app框架。
如果您想手动创建,也可以这样做。

您可以删除App.test.jssetupTests.jslogo.svg
然后App.js按如下方式清理:


import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      Hello I am react app.
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

运行yarn startnpm start在本地主机上运行开发服务器。

项目设置到此就完成了。让我们继续下一步。

让我们构建我们的应用程序。

将我们的应用程序分解成功能。

  • 首先src/App.js,创建一个名为的函数,calculateWinner从数组中获取获胜者。

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
}

Enter fullscreen mode Exit fullscreen mode

我们在线阵列中存储了获胜者的线条模式,如果我们在游戏的任何时候看到这些模式中的任何一个,我们就会宣布获胜者。

  • 为了计算下一回合,创建一个名为的函数calculateNextValue

function calculateNextValue(squares) {
  return squares.filter(Boolean).length % 2 === 0 ? 'X' : 'O'
}

Enter fullscreen mode Exit fullscreen mode
  • 然后,我们可以通过创建下一个函数来检查游戏的当前状态。

function calculateStatus(winner, squares, nextValue) {
  return winner
    ? `Winner: ${winner}`
    : squares.every(Boolean)
    ? `Scratch: Cat's game`
    : `Next player: ${nextValue}`
}

Enter fullscreen mode Exit fullscreen mode

现在App.css让我们为我们的游戏板写一些样式


.game {
  font: 14px 'Century Gothic', Futura, sans-serif;
  margin: 20px;
  min-height: 260px;
}

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

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

.status {
  margin-bottom: 10px;
}

.restart {
  margin-top: 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;
  background: #ddd;
}

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

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

Enter fullscreen mode Exit fullscreen mode

现在让我们在 App.js 中创建游戏板

function Board() {
  const [squares, setSquares] = React.useState(Array(9).fill(null))

  const nextValue = calculateNextValue(squares)
  const winner = calculateWinner(squares)
  const status = calculateStatus(winner, squares, nextValue)

  function selectSquare(square) {
    if (winner || squares[square]) {
      return
    }
    const squaresCopy = [...squares]
    squaresCopy[square] = nextValue
    setSquares(squaresCopy)
  }

  function restart() {
    setSquares(Array(9).fill(null))
  }

  function renderSquare(i) {
    return (
      <button className="square" onClick={() => selectSquare(i)}>
        {squares[i]}
      </button>
    )
  }

  return (
    <div>
      <div className="status">{status}</div>
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
      <button className="restart" onClick={restart}>
        restart
      </button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

现在创建一个Game()函数并将我们的Board()组件放入

其中。


function Game() {
  return (
    <div className="game">
      <div className="game-board">
        <Board />
      </div>
    </div>
  )
}

Enter fullscreen mode Exit fullscreen mode

然后Game()在里面渲染函数App()


function App() {
  return <Game />
}
Enter fullscreen mode Exit fullscreen mode

最后我们的App.js看起来应该是这样的。


import React from 'react';
import "./App.css";

function Board() {
  const [squares, setSquares] = React.useState(Array(9).fill(null))

  const nextValue = calculateNextValue(squares)
  const winner = calculateWinner(squares)
  const status = calculateStatus(winner, squares, nextValue)

  function selectSquare(square) {
    if (winner || squares[square]) {
      return
    }
    const squaresCopy = [...squares]
    squaresCopy[square] = nextValue
    setSquares(squaresCopy)
  }

  function restart() {
    setSquares(Array(9).fill(null))
  }

  function renderSquare(i) {
    return (
      <button className="square" onClick={() => selectSquare(i)}>
        {squares[i]}
      </button>
    )
  }

  return (
    <div>
      <div className="status">{status}</div>
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
      <button className="restart" onClick={restart}>
        restart
      </button>
    </div>
  )
}

function Game() {
  return (
    <div className="game">
      <div className="game-board">
        <Board />
      </div>
    </div>
  )
}

function calculateStatus(winner, squares, nextValue) {
  return winner
    ? `Winner: ${winner}`
    : squares.every(Boolean)
    ? `Scratch: Cat's game`
    : `Next player: ${nextValue}`
}

function calculateNextValue(squares) {
  return squares.filter(Boolean).length % 2 === 0 ? 'X' : 'O'
}

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
}

function App() {
  return <Game />
}

export default App
Enter fullscreen mode Exit fullscreen mode

就这样。我们完成了。

💡 附加功能

您可以使用以下钩子将游戏的方块保存到浏览器本地主机:


function useLocalStorageState(
  key,
  defaultValue = '',
  {serialize = JSON.stringify, deserialize = JSON.parse} = {},
) {
  const [state, setState] = React.useState(() => {
    const valueInLocalStorage = window.localStorage.getItem(key)
    if (valueInLocalStorage) {
      return deserialize(valueInLocalStorage)
    }
    return typeof defaultValue === 'function' ? defaultValue() : defaultValue
  })

  const prevKeyRef = React.useRef(key)

  React.useEffect(() => {
    const prevKey = prevKeyRef.current
    if (prevKey !== key) {
      window.localStorage.removeItem(prevKey)
    }
    prevKeyRef.current = key
    window.localStorage.setItem(key, serialize(state))
  }, [key, state, serialize])

  return [state, setState]
}

export {useLocalStorageState}
Enter fullscreen mode Exit fullscreen mode

谢谢阅读。

编码愉快😍。

文章来源:https://dev.to/asyncnavi/let-s-create-a-tic-tac-toe-with-react-440g
PREV
纯 CSS 女王的棋局
NEXT
初级到高级 Web 开发人员路线图:细分