https://react.docschina.org/learn
快速入门
- 组件创建和嵌套
- 添加标签和样式
- 显示数据
- 渲染条件和列表
- 事件响应并更新界面
- 组件之间共享数据
基础概念
- React是由组件构成
- 一个组件是UI的一部分
- 组件拥有自己的逻辑和外观
- 组件可以是一个标签,也可以是整个页面
- React组件是返回标签的JavaScript函数
React 组件必须以大写字母开头,而 HTML 标签则必须是小写字母。
export default 关键字指定了文件中的主要组件。如果你对 JavaScript 某些语法不熟悉,可以参考 MDN 和 javascript.info。
事件处理
javascript
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
点我
</button>
);
}
注意!!onClick={handleClick} 的结尾没有小括号!。不要 调用 事件处理函数:你只需 把函数传递给事件 即可。当用户点击按钮时 React 会调用你传递的事件处理函数。
组件状态值传递:props
以井字棋为例
https://react.docschina.org/learn/tutorial-tic-tac-toe
别瞧不起这一个小的项目,我们跟着官方的教程实际上能学到很多代码设计和组织的思想。同时可以增强对react框架的理解。

游戏规则:
-
初始状态:空棋盘
\] \[ \] \[
\] \[ \] \[
\] \[ \] \[
-
玩家1(X)先手,选择一个空位放置 X
-
玩家2(O)在剩余空位放置 O
-
双方轮流落子,直到:
- 有玩家连成一线 → 该玩家获胜
- 所有格子填满且无人获胜 → 平局
(1)构建静态页面
包含井字棋 3X3 的静态方格显示。创建Square 组件表示一个单一的数字方格,Bord 组件则将九个数字方格组件集成到一起。
(2)交互式规则设计
当用户点击对应方格的时候,它显示的内容会发生改变。
当你单击它的时候,Square 组件需要显示"X"。在 Square 内部声明一个名为 handleClick 的函数。然后,将 onClick 添加到由 Square 返回的 JSX 元素的 button 的 props 中
javascript
function Square({ value }) {
function handleClick() {
console.log('clicked!');
}
return (
<button
className="square"
onClick={handleClick}
>
{value}
</button>
);
}
(3)状态管理state
我们希望Square能够记住用户的点击并显示正确的数据
javascript
function Square() {
const [value, setValue] = useState(null);
function handleClick() {
setValue('X');
}
return (
<button
className="square"
onClick={handleClick}
>
{value}
</button>
);
}
每个 Square 都有自己的 state:存储在每个 Square 中的 value 完全独立于其他的 Square。当你在组件中调用 set 函数时,React 也会自动更新内部的子组件。
(4)游戏规则逻辑
对相邻的两次点击事件,交互显示"X","O"。
目前,每个 Square 组件都维护着游戏 state 的一部分。要检查井字棋游戏中的赢家,Board 需要以某种方式知道 9 个 Square 组件中每个组件的 state。
javascript
import { useState } from 'react';
function Square({ value }) {
return <button className="square">{value}</button>;
}
export default function Board() {
const [squares, setSquares] = useState(Array(9).fill(null));
return (
<>
<div className="board-row">
<Square value={squares[0]} />
<Square value={squares[1]} />
<Square value={squares[2]} />
</div>
<div className="board-row">
<Square value={squares[3]} />
<Square value={squares[4]} />
<Square value={squares[5]} />
</div>
<div className="board-row">
<Square value={squares[6]} />
<Square value={squares[7]} />
<Square value={squares[8]} />
</div>
</>
);
}
更改单击 Square 时发生的情况
Board 组件向下传递一个函数到 Square 组件,然后让 Square 在单击方块时调用该函数。我们将从单击 Square 组件时将调用的函数开始。调用该函数 onSquareClick
javascript
function Square({ value, onSquareClick }) {
return (
<button className="square" onClick={onSquareClick}>
{value}
</button>
);
}
(5)JavaScript数组
const copy = Squares.slice();// 不提供参数,复制整个数组const getFirstN = (arr, n) => arr.slice(0, n);// 获取前n个元素
(6)传递函数而非直接调用
下面这条代码会出现错误❌:
javascript
<Square value={squares[0]} onSquareClick={handleClick(0)} />
为什么会是这样呢?handleClick(0) 调用将成为渲染 Board 组件的一部分。因为 handleClick(0) 通过调用 setSquares 改变了棋盘组件的 state,所以你的整个棋盘组件将再次重新渲染。但这再次运行了 handleClick(0),导致无限循环:
// 当你渲染 Board 组件时:
- Board() 函数被调用执行
- 遇到 handleClick(0) → 立即执行函数
- handleClick(0) 内部调用 setSquares() 更新 state
- React 检测到 state 变化,安排重新渲染
- Board() 再次被调用执行
- 再次遇到 handleClick(0) → 立即执行
- 无限循环开始...
// ✅ 正确:函数传递(作为引用)
onSquareClick={() => handleClick(0)}
// 这创建了一个新函数,当点击时才执行 handleClick(0)
在 JSX 中传递事件处理函数时,应该传递函数引用或返回函数的函数,而不是函数调用的结果。React 组件在渲染时应该是"纯"的,不应该有直接改变 state 的副作用操作。
javascript
import { useState } from 'react';
function Square({ value, onSquareClick }) {
return (
<button className="square" onClick={onSquareClick}>
{value}
</button>
);
}
export default function Board() {
const [squares, setSquares] = useState(Array(9).fill(null));
function handleClick(i) {
const nextSquares = squares.slice();
nextSquares[i] = 'X';
setSquares(nextSquares);
}
return (
<>
<div className="board-row">
<Square value={squares[0]} onSquareClick={() => handleClick(0)} />
<Square value={squares[1]} onSquareClick={() => handleClick(1)} />
<Square value={squares[2]} onSquareClick={() => handleClick(2)} />
</div>
<div className="board-row">
<Square value={squares[3]} onSquareClick={() => handleClick(3)} />
<Square value={squares[4]} onSquareClick={() => handleClick(4)} />
<Square value={squares[5]} onSquareClick={() => handleClick(5)} />
</div>
<div className="board-row">
<Square value={squares[6]} onSquareClick={() => handleClick(6)} />
<Square value={squares[7]} onSquareClick={() => handleClick(7)} />
<Square value={squares[8]} onSquareClick={() => handleClick(8)} />
</div>
</>
);
}
(7)不变性
- 不变性使复杂的功能更容易实现。
- 使用具有所需变化的新副本替换数据
- 不变性使得组件比较其数据是否已更改的成本非常低。
(8)交替落子
在Board组件中定义一个xIsNext状态,它接收一个布尔变量,控制状态显示。
为了避免一个数字方格多次点击出现状态漂移,我们在显示状态之前需要判断数字方格是否有数据存在。
(9)宣布获胜者
定义一个辅助函数calculateWinner,它接受 9 个方块的数组,检查获胜者并根据需要返回 'X'、'O' 或 null。由于我们知道所有的获胜情况,因此可以定义一个数组,通过将Squares传入的三个状态量与"获胜数组"中的情况进行比较,以此得到最终的赢家。
代码汇总
javascript
import { useState } from 'react';
function Square({value, onSquareClick}) {
return (
<button className="square" onClick={onSquareClick}>
{value}
</button>
);
}
export default function Board() {
const [xIsNext, setXIsNext] = useState(true);
const [squares, setSquares] = useState(Array(9).fill(null));
function handleClick(i) {
if (calculateWinner(squares) || squares[i]) {
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = 'X';
} else {
nextSquares[i] = 'O';
}
setSquares(nextSquares);
setXIsNext(!xIsNext);
}
const winner = calculateWinner(squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square value={squares[0]} onSquareClick={() => handleClick(0)} />
<Square value={squares[1]} onSquareClick={() => handleClick(1)} />
<Square value={squares[2]} onSquareClick={() => handleClick(2)} />
</div>
<div className="board-row">
<Square value={squares[3]} onSquareClick={() => handleClick(3)} />
<Square value={squares[4]} onSquareClick={() => handleClick(4)} />
<Square value={squares[5]} onSquareClick={() => handleClick(5)} />
</div>
<div className="board-row">
<Square value={squares[6]} onSquareClick={() => handleClick(6)} />
<Square value={squares[7]} onSquareClick={() => handleClick(7)} />
<Square value={squares[8]} onSquareClick={() => handleClick(8)} />
</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;
}