目录
- 1 React 简介
- 2 实现井字棋小游戏
- 2.1 初始化
- 2.2 props
- 2.3 setState
- 2.4 状态提升
- 2.5 副本
- 2.6 简化组件
- 2.7 key
- 2.8 小结
- 3 核心概念
1 React 简介
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库
官网提供了两种学习思路
如果你喜欢边做边学,请从实践教程开始。
如果你喜欢一步步学习概念,请从 Hello World 开始。
以下根据边学边做的方式,提供一种学习路线
2 实现井字棋小游戏
首先,根据官方提供的入门教程使用 react 实现井字棋小游戏,对 react 的相关概念有初步的了解,从而更好地上手 react。
最终完成的代码👉资源
你在实现过程中是否也遇到问题,有没有似懂非懂的时候?下面列举出一些点:
2.1 初始化
新建一个 react 项目
npx create-react-app my-app
2.2 props
在给棋盘增加数字这步,this.props
代表什么,哪里来的?
js">class Square extends React.Component {
render() {
return <button className="square">{this.props.value}</button>;
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
render() {
...
}
}
- 把它输出试试
js">class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => { console.log(this.props, "--this.props"); }}
>
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
render() {
...
}
}
点击 3 控制台输出以下信息:
this.props
是 Board
中 <Square value={i} />
传入的 value={i}
变成的对象形式 {value: i}
- 那把 value 换一下试试
js">class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => { console.log(this.props, "--this.props"); }}
>
{this.props.title} // value 换成 title
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square title={i} />; // value 换成 title
}
render() {
...
}
}
点击 3 控制台输出:
因此 Square
中的 props
是 <Square />
标签中传入的参数对象
2.3 setState
把棋盘上的数字换成点击显示X,这里的 this.setState
是什么
js">class Square extends React.Component {
// 构造函数,props 为接收的参数
constructor(props) {
super(props); // 定义子类的构造函数时,都需要调用 super 方法
this.state = { value: null }; // 给 Square 类定义一个属性 state
}
render() {
return (
// this.setState 更新 state 的数据
<button className="square" onClick={() => this.setState({ value: "x" })}>
{this.state.value}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />; // Square 的 this.props = {value: i}
}
render() {
...
}
}
关于setState官方文档是这样解释的:
setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。
这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
也就是 setState
将 state
从 { value: null }
更新为 { value: "x" }
2.4 状态提升
js">class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => this.props.onClick()}>
{this.props.value} // 用 props 中的 value
</button>
);
}
}
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null), // 初始化数组
};
}
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = "X";
this.setState({ squares: squares });
}
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}
render() {
...
}
}
当前 state 没有保存在单个的 Square 组件中,而是保存在了 Board 组件中。
每当 Board 的 state 发生变化的时候,这些 Square 组件都会重新渲染一次。
把所有 Square 的 state 保存在 Board 组件中可以让我们在将来判断出游戏的胜者。
因为 Square 组件不再持有 state,因此每次它们被点击的时候,Square 组件就会从 Board 组件中接收值,并且通知 Board 组件。
在 React 术语中,我们把目前的 Square 组件称做“受控组件”。在这种情况下,Board 组件完全控制了 Square 组件。
状态提升,主要是两点变化:
- state 从在 Square 保存 改为在 Board 保存
- Square 组件从 Board 组件中接收 state 值
2.5 副本
js">const squares = this.state.squares.slice();
这里 slice
什么用?
用 slice()
方法创建了 squares
数组的一个副本,而不是直接在现有的数组上进行修改,这样可以便于回溯历史数据
2.6 简化组件
组件只包含一个 render 方法,并且不包含 state,使用函数组件会更简单
js">class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => this.props.onClick()}>
{this.props.value}
</button>
);
}
}
简化为
js">function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
2.7 key
js">class Game extends React.Component {
constructor(props) {
...
}
handleClick(i) {
...
}
jumpTo(step) {
...
}
render() {
...
const moves = history.map((step, move) => {
const desc = move ? "Go to move #" + move : "Go to game start";
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
...
return (
...
);
}
}
每当一个列表重新渲染时,React 会根据每一项列表元素的 key
来检索上一次渲染时与每个 key
所匹配的列表项。
- 发现不存在的
key
,那么就会创建出一个新的组件。 - 发现少了一个
key
,那么就会销毁之前对应的组件。 - 发现一个变化的
key
,那么这个key
所在的组件会被销毁,然后使用新的state
重新创建一份。
key
是 React 中一个特殊的保留属性(还有一个是ref
,拥有更高级的特性)。
当 React 元素被创建出来的时候,React 会提取出key
属性,然后把key
直接存储在返回的元素上。
虽然
key
看起来好像是props
中的一个,但是你不能通过this.props.key
来获取key
。
React 会通过key
来自动判断哪些组件需要更新。组件是不能访问到它的key
的。
- 每次只要构建动态列表的时候,都要指定一个合适的
key
。- 如果你没有找到一个合适的
key
,那么你就需要考虑重新整理你的数据结构了,这样才能有合适的key
。 - 如果你没有指定任何
key
,React 会发出警告,并且会把数组的索引当作默认的key
。- 但是如果想要对列表进行重新排序、新增、删除操作时,把数组索引作为
key
是有问题的。 - 显式地使用
key={i}
来指定key
确实会消除警告,但是仍然和数组索引存在同样的问题,所以大多数情况下最好不要这么做。
- 但是如果想要对列表进行重新排序、新增、删除操作时,把数组索引作为
- 如果你没有找到一个合适的
2.8 小结
将实现井字棋过程中的知识点简洁地列出:
- 新建一个 react 项目
npx create-react-app 项目名
props
:组件标签 中传入的参数对象setState()
:更新state
的数据- 状态提升:
state
从在子组件
保存改为在父组件
保存,子组件
从父组件
中接收state
值 - 副本:用
slice()
方法创建数组的一个副本,而不是直接在现有的数组上进行修改,便于回溯历史数据 - 简化组件:组件只包含一个
render
方法,并且不包含state
,使用函数组件会更简单 key
:每当一个列表重新渲染时,React 会根据每一项列表元素的key
来检索上一次渲染时与每个key
所匹配的列表项- 一定要指定一个合适的
key
,最好不要用索引
- 一定要指定一个合适的
还有一个命名规范,先记录以下,也许之后会用到:
- 命名规范:React 命名规范,通常
- 将代表事件的监听
prop
命名为on[Event]
- 将处理事件的监听方法命名为
handle[Event]
这样的格式
- 将代表事件的监听
3 核心概念
React 官网
持续更新,未完待续…