React 01
1 React 组件必须返回单个 JSX 元素
React 组件必须返回单个 JSX 元素,不能像两个按钮那样返回多个相邻的 JSX 元素。要解决此问题,可以使用 Fragment(<>
与 </>
)包裹多个相邻的 JSX 元素,如下所示:
export default function Square() {return (<><button className="square">X</button><button className="square">X</button></>);
}
2 React JSX
React JSX | 菜鸟教程https://www.runoob.com/react/react-jsx.html
我们来逐步拆解这段 JSX 代码,理解它的作用:
1. 组件定义
export default function Board() {return (// ... JSX 内容);
}
- 这是一个 React 函数组件,名为
Board
。 export default
表示将这个组件作为默认导出,方便其他文件引入使用。
2. Fragment(片段)
<>{/* 其他内容 */}
</>
- 这是 React 的 Fragment(片段)语法。
- 它的作用是在不额外添加嵌套
div
等标签的情况下,将多个元素组合在一起返回。因为 React 组件的返回值必须是一个单一的根元素,Fragment 就解决了这个问题,且不会在 DOM 中添加额外的节点。
3. 渲染行(board-row
)和方块(Square
)
<div className="board-row"><Square /><Square /><Square />
</div>
- 这里的
div
标签,className="board-row"
是给这个div
添加 CSS 类名,用于设置样式(比如让一行内的元素排列整齐等)。 <Square />
是在渲染Square
组件。这里重复渲染了多个Square
组件,构建出类似井字棋(Tic-Tac-Toe)棋盘的结构,3 行,每行 3 个方块,总共 9 个方块。
整体作用
这段代码定义了一个 Board
组件,它通过 JSX 语法,使用 div
标签分组(每行一个 div
,类名为 board-row
),并在每个分组里渲染 Square
组件,最终呈现出一个 3×3 的棋盘结构,是构建井字棋等类似棋盘类应用界面的一部分。
你提到的 <Square />
这一行是在 使用 Square 组件,它的作用和后面的 <button>
标签类似,都是在页面上渲染一个元素,只是实现方式不同。
具体可以这样理解:
-
组件的复用:你前面已经定义了
Square
函数组件:function Square(){return <button className = "square">1</button> }
这个组件的功能是返回一个带有
square
类名的按钮,按钮内容是 "1"。当你在
Board
组件中写<Square />
时,就相当于把上面定义的这个按钮「插入」到了当前位置,和直接写<button className="square">1</button>
的效果是一样的。 -
与直接写 button 的区别:后面的
<button className="square">2</button>
是直接写死的按钮,而<Square />
是通过组件复用的方式渲染的按钮。这种组件化的好处是:如果以后需要修改所有方块的样式或行为,只需要改
Square
组件的代码,而不用逐个修改每个按钮。 -
为什么这样写:从代码来看,这可能是一个逐步改造的过程 —— 先用直接写死的按钮搭建结构,再把重复的按钮逻辑提取成
Square
组件。最终目标可能是把所有<button>
都替换成<Square />
组件,让代码更简洁、更易于维护。
简单说,<Square />
就像是一个「按钮模板」,用它可以快速生成具有相同样式和基础功能的按钮。
3 React 中如何通过 props
(属性)传递数据
这部分内容是在讲解 React 中如何通过 props
(属性)传递数据,以及 JSX 中如何正确使用 JavaScript 变量。
1. 最初的问题
最开始,Square
组件直接返回固定内容为 1
的按钮:
function Square() {return <button className="square">1</button>;
}
当在 Board
组件中多次使用 <Square />
时,所有方块都显示 1
,没有了原本不同的编号,这不符合需求。
2. 尝试用 props
传递数据
为了让每个 Square
显示不同的内容,需要从父组件 Board
向子组件 Square
传递数据,这就要用到 props
。首先修改 Square
组件,接收 value
属性:
function Square({ value }) {return <button className="square">1</button>;
}
这里 { value }
是解构赋值,从 props
对象中取出 value
属性。但此时按钮里还是固定的 1
,因为没有使用传递过来的 value
。
3. 错误的显示结果
如果直接写 return <button className="square">value</button>;
,页面上会显示字符串 "value"
,而不是 value
变量所代表的值。这是因为在 JSX 中,普通的字符串会被当作文本直接渲染,而不是 JavaScript 变量。
4. 正确的 JSX 语法
要在 JSX 中使用 JavaScript 变量(这里是 value
),需要用大括号 {}
把变量包起来,这样就会从 JSX「转义」到 JavaScript,渲染变量的值:
function Square({ value }) {return <button className="square">{value}</button>;
}
这样,当父组件 Board
给 Square
传递不同的 value
属性时,每个 Square
就能显示对应的内容了。比如 <Square value="2" />
,这个 Square
组件的按钮就会显示 2
。
简单来说,大括号 {}
在 JSX 中是一个重要的标记,它告诉 React:这里要插入的是 JavaScript 表达式(变量、运算等),而不是普通的字符串文本。
4 useState
这段内容主要讲解了在 React 函数组件中使用 useState
钩子来管理组件的状态,让 Square
组件能够 “记住” 是否被点击过,并在点击时更新显示内容。
1. useState
是什么
useState
是 React 提供的一个钩子函数(Hook),专门用于在函数组件中添加状态管理功能。在类组件中,我们可以通过 this.state
和 this.setState
来管理状态,而在函数组件中,就需要借助 useState
等钩子。
2. 导入 useState
import { useState } from 'react';
这行代码的作用是从 react
库中导入 useState
函数,只有导入后,才能在组件中使用它来管理状态。
3. 在 Square
组件中使用 useState
function Square() {const [value, setValue] = useState(null);// ... 其他代码
}
useState(null)
:调用useState
并传入初始值null
。useState
会返回一个数组,数组的第一个元素是状态变量(这里命名为value
),第二个元素是用来更新状态变量的函数(这里命名为setValue
)。- 初始时,
value
的值为null
,因为我们希望Square
组件一开始没有被点击,没有填充内容。
4. 为什么要移除 value props
之前 Square
组件的内容是通过父组件 Board
传递 value
属性来设置的。现在要让 Square
自己 “记住” 点击状态,所以不再需要从父组件接收 value
属性,而是由组件自身的状态 value
来管理显示内容。
5. 后续逻辑(结合点击事件)
接下来通常会编写点击事件处理函数 handleClick
,在这个函数中调用 setValue
来更新 value
的值。比如,当 Square
被点击时,将 value
设置为 'X'
,这样就能实现点击后用 'X'
填充方块的效果,代码大概会是这样:
function handleClick() {setValue('X');
}
然后把 handleClick
函数绑定到按钮的 onClick
事件上:
return <button className="square" onClick={handleClick}>{value}</button>;
这样,当用户点击 Square
对应的按钮时,handleClick
函数被触发,value
被更新为 'X'
,按钮的显示内容也会随之改变。
总结一下,这部分内容核心是引入 useState
钩子,让 Square
组件拥有自己的状态,从而能够响应点击事件并更新自身显示内容,实现 “记住” 点击状态的功能。
5
JavaScript Array slice() 方法 | 菜鸟教程https://www.runoob.com/jsref/jsref-slice-array.html
1. 整体背景
这段代码是在实现一个类似井字棋(Tic-Tac-Toe)的棋盘逻辑。Board
组件管理着棋盘的状态(squares
数组,用来表示每个格子的内容,初始都是 null
,代表空),handleClick
函数是用来处理点击事件,更新棋盘状态的。
2. useState
部分
const [squares, setSquares] = useState(Array(9).fill(null));
- 这里使用 React 的
useState
钩子来定义状态。 squares
是状态变量,它是一个长度为 9 的数组(对应井字棋 3x3 的 9 个格子),每个元素初始值为null
,表示格子初始为空。setSquares
是用来更新squares
状态的函数,调用它会触发组件重新渲染。
3. handleClick
函数
function handleClick() {const nextSquares = squares.slice();nextSquares[0] = "X";setSquares(nextSquares);
}
(1)slice()
方法的作用
squares.slice()
是 JavaScript 数组的slice
方法。- 如果
slice
方法没有传入任何参数(slice()
),它会创建一个原数组的浅拷贝(即复制一份原数组的内容,生成一个新数组)。 - 为什么要创建拷贝?因为在 React 中,状态是不可直接修改的(Immutable)。如果直接修改
squares
数组(比如squares[0] = "X"
),React 无法检测到状态的变化,也就不会触发重新渲染。所以需要先复制出一个新数组,修改新数组,再用新数组去更新状态。
(2)修改新数组并更新状态
nextSquares[0] = "X"
:在新创建的数组nextSquares
中,将索引为0
的元素(对应棋盘第一个格子)的值设为"X"
。setSquares(nextSquares)
:调用setSquares
函数,把修改后的nextSquares
数组设置为新的squares
状态。这会让 React 知道状态发生了变化,从而触发Board
组件及其子组件(比如Square
组件)重新渲染,页面上就能看到第一个格子显示为X
了。
4. 重新渲染的逻辑
当调用 setSquares(nextSquares)
后:
- React 会检测到
squares
状态发生了改变。 - 于是
Board
组件会重新执行渲染逻辑,它的子组件(那些展示squares
中对应格子内容的Square
组件)也会跟着重新渲染,这样页面上的棋盘就会更新为新的状态,我们就能看到第一个格子被填充为X
了。
简单总结一下,slice()
在这里的核心作用就是复制原数组,生成一个可修改的新数组,从而满足 React 状态不可直接修改的要求,让状态更新能被 React 检测到,进而触发组件重新渲染。
5 传递函数本身和立即调用函数
简单来说,就是:
- 当你写
onSquareClick={handleClick}
时,是传递函数本身,等需要的时候(比如用户点击)再去调用。 - 当你写
onSquareClick={handleClick()}
时,是立即调用函数,而不是等点击时才调用,这就会导致不符合预期的执行(比如无限循环、逻辑提前执行)。
文中用箭头函数 () => handleClick(0)
,就是为了传递一个 “待执行” 的逻辑:只有当用户点击时,才会执行 handleClick(0)
。