From 323c0fae59d19ca13b7ce524ac0e38549f28b64b Mon Sep 17 00:00:00 2001 From: aryecatcher <516371385@qq.com> Date: Fri, 24 Jul 2026 18:04:52 +0800 Subject: [PATCH] Refactor tic-tac-toe tutorial: use map, grid layout, and event delegation --- src/content/learn/tutorial-tic-tac-toe.md | 400 +++++++++------------- 1 file changed, 158 insertions(+), 242 deletions(-) diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index 7be70f088dd..76764286cf2 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -32,9 +32,9 @@ You can see what it will look like when you're finished here: ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -65,20 +65,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -161,22 +156,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -226,22 +218,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -469,22 +458,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -669,22 +655,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -864,22 +847,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -1034,22 +1014,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -1232,9 +1209,9 @@ This is what your code should look like: ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -1244,6 +1221,9 @@ export default function Board() { const [squares, setSquares] = useState(Array(9).fill(null)); function handleClick(i) { + if (squares[i]) { + return; + } const nextSquares = squares.slice(); nextSquares[i] = 'X'; setSquares(nextSquares); @@ -1251,20 +1231,15 @@ export default function Board() { return ( <> -
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -1285,22 +1260,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -1426,9 +1398,9 @@ Now you can only add `X`'s or `O`'s to empty squares! Here is what your code sho ```js src/App.js import { useState } from 'react'; -function Square({value, onSquareClick}) { +function Square({value, index}) { return ( - ); @@ -1454,20 +1426,15 @@ export default function Board() { return ( <> -
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -1488,22 +1455,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -1598,9 +1562,9 @@ Congratulations! You now have a working tic-tac-toe game. And you've just learne ```js src/App.js import { useState } from 'react'; -function Square({value, onSquareClick}) { +function Square({value, index}) { return ( - ); @@ -1635,20 +1599,15 @@ export default function Board() { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -1689,22 +1648,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -1876,9 +1832,9 @@ At this point, you've moved the state to live in the `Game` component, and the U ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -1909,20 +1865,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -1985,22 +1936,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -2087,9 +2035,9 @@ You'll fix this error in the next section. ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -2120,20 +2068,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -2214,22 +2157,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -2315,9 +2255,9 @@ const moves = history.map((squares, move) => { ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -2348,20 +2288,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -2443,22 +2378,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -2536,9 +2468,9 @@ If you click on any step in the game's history, the tic-tac-toe board should imm ```js src/App.js import { useState } from 'react'; -function Square({value, onSquareClick}) { +function Square({value, index}) { return ( - ); @@ -2569,20 +2501,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -2667,22 +2594,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status { @@ -2746,9 +2670,9 @@ Check out the final result here: ```js src/App.js import { useState } from 'react'; -function Square({ value, onSquareClick }) { +function Square({ value, index }) { return ( - ); @@ -2779,20 +2703,15 @@ function Board({ xIsNext, squares, onPlay }) { return ( <>
{status}
-
- handleClick(0)} /> - handleClick(1)} /> - handleClick(2)} /> -
-
- handleClick(3)} /> - handleClick(4)} /> - handleClick(5)} /> -
-
- handleClick(6)} /> - handleClick(7)} /> - handleClick(8)} /> +
{ + const target = e.target; + if (target.tagName === 'BUTTON') { + handleClick(target.dataset.index); + } + }}> + {squares.map((value, i) => ( + + ))}
); @@ -2875,22 +2794,19 @@ body { .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; } -.board-row:after { - clear: both; - content: ''; - display: table; +.board { + display: grid; + grid-template-columns: repeat(3, auto); + width: fit-content; } .status {