你已经准备好了,让我们先大致了解一下 React 吧!
React 是什么?
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
React 中拥有多种不同类型的组件,我们先从 React.Component
的子类开始介绍:
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
// 用法示例: <ShoppingList name="Mark" />
我们马上会讨论这些又奇怪、又像 XML 的标签。我们通过使用组件来告诉 React 我们希望在屏幕上看到什么。当数据发生改变时,React 会高效地更新并重新渲染我们的组件。
其中,ShoppingList 是一个 React 组件类,或者说是一个 React 组件类型。一个组件接收一些参数,我们把这些参数叫做 props
(“props” 是 “properties” 简写),然后通过 render
方法返回需要展示在屏幕上的视图的层次结构。
render
方法的返回值描述了你希望在屏幕上看到的内容。React 根据描述,然后把结果展示出来。更具体地来说,render
返回了一个 React 元素,这是一种对渲染内容的轻量级描述。大多数的 React 开发者使用了一种名为 “JSX” 的特殊语法,JSX 可以让你更轻松地书写这些结构。语法 <div />
会被编译成 React.createElement('div')
。上述的代码等同于:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */),
React.createElement('ul', /* ... ul children ... */)
);
如果你对这个比较感兴趣,可以查阅 API 文档了解有关 createElement()
更详细的用法。但在接下来的教程中,我们并不会直接使用这个方法,而是继续使用 JSX。
在 JSX 中你可以任意使用 JavaScript 表达式,只需要用一个大括号把表达式括起来。每一个 React 元素事实上都是一个 JavaScript 对象,你可以在你的程序中把它保存在变量中或者作为参数传递。
前文中的 ShoppingList
组件只会渲染一些内置的 DOM 组件,如<div />
、<li />
等。但是你也可以组合和渲染自定义的 React 组件。例如,你可以通过 <ShoppingList />
来表示整个购物清单组件。每个组件都是封装好的,并且可以单独运行,这样你就可以通过组合简单的组件来构建复杂的 UI 界面。
阅读初始代码
在你的工程文件夹下打开 src/index.js
(你已经在前面的环境准备中创建过这个文件了)。
这些初始代码是我们要开发的小游戏的基础代码。我们已经提供了 CSS 样式,这样你只需要关注使用 React 来开发这个井字棋了。
通过阅读代码,你可以看到我们有三个 React 组件:
- Square
- Board
- Game
Square 组件渲染了一个单独的 <button>
。Board 组件渲染了 9 个方块。Game 组件渲染了含有默认值的一个棋盘,我们一会儿会修改这些值。到目前为止还没有可以交互的组件。
通过 Props 传递数据
让我们试试水,尝试将数据从 Board 组件传递到 Square 组件中。
我们强烈建议你动手编写本教程中的代码,不要使用复制/粘贴。这将加深你对 React 的记忆和理解。
在 Board 组件的 renderSquare
方法中,我们将代码改写成下面这样,传递一个名为 value
的 prop 到 Square 当中:
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
}
修改 Square 组件中的 render
方法,把 {/* TODO */}
替换为 {this.props.value}
,以显示上文中传入的值:
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
修改前:
修改后:在渲染结果中,你可以看到每个方格中都有一个数字。
恭喜你!你刚刚成功地把一个 prop 从父组件 Board “传递”给了子组件 Square。在 React 应用中,数据通过 props 的传递,从父组件流向子组件。
给组件添加交互功能
接下来我们试着让棋盘的每一个格子在点击之后能落下一颗 “X” 作为棋子。 首先,我们把 Square 组件中 render()
方法的返回值中的 button 标签修改为如下内容:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function() { console.log('click'); }}>
{this.props.value}
</button>
);
}
}
如果此刻点击某个格子,将在浏览器的开发者工具中打印 ‘click’。
为了少输入代码,同时为了避免 this 造成的困扰,我们在这里使用 箭头函数 来进行事件处理,如下所示:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => console.log('click')}>
{this.props.value}
</button>
);
}
}
注意:此处使用了 onClick={() => console.log('click')}
的方式向 onClick
这个 prop 传入一个函数。 React 将在单击时调用此函数。但很多人经常忘记编写 () =>
,而写成了 onClick={console.log('click')}
,这种常见的错误会导致每次这个组件渲染的时候都会触发控制台输出。
接下来,我们希望 Square 组件可以“记住”它被点击过,然后用 “X” 来填充对应的方格。我们用 state 来实现所谓“记忆”的功能。
可以通过在 React 组件的构造函数中设置 this.state
来初始化 state。this.state
应该被视为一个组件的私有属性。我们在 this.state
中存储当前每个方格(Square)的值,并且在每次方格被点击的时候改变这个值。
首先,我们向这个 class 中添加一个构造函数,用来初始化 state:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => console.log('click')}>
{this.props.value}
</button>
);
}
}
注意
在JavaScript class
中,每次你定义其子类的构造函数时,都需要调用super
方法。因此,在所有含有构造函数的的 React 组件中,构造函数必须以super(props)
开头。
现在,我们来修改一下 Square 组件的 render
方法,这样,每当方格被点击的时候,就可以显示当前 state 的值了:
- 在
<button>
标签中,把this.props.value
替换为this.state.value
。 - 将
onClick={...}
事件监听函数替换为onClick={() => this.setState({value: 'X'})}
。 - 为了更好的可读性,将
className
和onClick
的 prop 分两行书写。
修改之后,Square 组件中 render
方法的返回值中的 <button>
标签就变成了下面这样:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button
className="square"
onClick={() => this.setState({value: 'X'})}
>
{this.state.value}
</button>
);
}
}
在 Square 组件 render
方法中的 onClick
事件监听函数中调用 this.setState
,我们就可以在每次 <button>
被点击的时候通知 React 去重新渲染 Square 组件。组件更新之后,Square 组件的 this.state.value
的值会变为 'X'
,因此,我们在游戏棋盘上就能看见 X
了。点击任意一个方格,X 就会出现了。
每次在组件中调用 setState
时,React 都会自动更新其子组件。
开发者工具
在 Chrome 或者 Firefox 中安装扩展 React Devtools 可以让你在浏览器开发者工具中查看 React 的组件树。
你还可以在 React DevTools 中检查 React 组件的 state 和 props。
安装 React DevTools 之后,右键点击页面的任何一个元素,然后选择“查看”,这样就能打开浏览器的开发者工具了,并且工具栏最后会多展示一个 React 的选项卡(包含 “⚛️ Components” 和 “⚛️ Profiler”)。你可以使用 “⚛️ Components” 来检查组件树。