目录
1 渲染 HTML 元素为 DOM 树
2 了解 JSX 的自动闭合
3 创建组件
3.1 JS函数创建组件----无状态组件
3.2 class创建组件
3.3 用组合的方式创建一个 React 组件
3.4 创建一个有状态的组件
3.4.1 用 this.setState 设置状态
3.4.2 将 this 绑定到 Class 方法上
3.4.3 使用 State 切换元素
4 将 class 组件渲染到 DOM 树
5 传参
5.1 将 Props 传递给无状态函数组件
5.2 传递一个数组作为 Props
5.3 使用默认的 Props
5.4 覆盖默认的 Props
5.5 使用 PropTypes 来定义 Props 的类型
5.6 使用 this.props 访问 Props
5.8 传递回调作为 Props
6 小例子
6.1 一个简单的计数器
6.2 创建一个可以控制的输入框
6.3 创建一个可以控制的表单
7 其他
7.1 使用生命周期方法:componentWillMount
7.2 使用生命周期方法:componentDidMount
7.3 添加事件侦听器
7.4 使用 shouldComponentUpdate 优化重新渲染
7.5 内联样式
7.6 使用 && 获得更简洁的条件
7.7 使用三元表达式进行条件渲染
7.8 根据 Props 有条件地渲染
7.9 使用 Array.map() 动态渲染元素
7.10 给同级元素一个唯一的键属性
7.11 使用 Array.Filter() 动态过滤数组
7.12 用 renderToString 在服务器上渲染 React
1 渲染 HTML 元素为 DOM 树
ReactDOM 提供了一个简单的方法来将 React 元素呈现给 DOM,如下所示:
ReactDOM.render(componentToRender, targetNode)
其中第一个参数是要渲染的 React 元素或组件,第二个参数是组件将要渲染到的 DOM 节点。
并且必须在 JSX 元素声明之后调用 ReactDOM.render()
,就像在使用变量之前必须声明它一样。
示例:
代码编辑器有一个简单的 JSX 组件。使用 ReactDOM.render()
方法将该组件渲染到页面。 可以将定义好的 JSX 元素直接作为第一个参数传入,然后使用 document.getElementById()
来选择要渲染到的 DOM 节点, 在这个示例中,请渲染到 id='challenge-node'
的 div
中。 确保没有修改 JSX
常量。
const JSX = (
Hello World
Lets render this to the DOM
);
// 修改这行下面的代码
ReactDOM.render(
JSX,document.getElementById('challenge-node')
);
2 了解 JSX 的自动闭合
关于属性大小写问题:
JSX中使用驼峰法,因为小写是原生HTML的关键字,比如添加class属性,HTML中使用class,JSX中使用className。
JSX 不同于 HTML 的另一个重要方面是自闭合标签。
在HTML中,几乎所有的标签都有一个开始和结束标签:,结束标签在你要关闭的标签名之前始终具有正斜杠。 但是,HTML 中有一些称为 “自闭合标签” 的特殊实例,它们在另一个标签开始之前,不需要开始和结束标签都存在。
例如,换行标签可以写成
或者
,但是不应该写成
,因为它不包含任何内容。
在 JSX 中,规则略有不同。 任何 JSX 元素都可以使用自闭合标签编写,并且每个元素都必须关闭。
例如,为了通过编译换行标签必须始终编写为
。
另一方面 组件是 React 的核心,React 中的所有内容都是一个组件。 有两种方法可以创建 React 组件。 第一种方法是使用 JavaScript 函数。 以这种方式定义组件会创建无状态功能组件。 要用函数创建组件,只需编写一个返回 JSX 或 翻译完成后, 因为 JSX 组件代表 HTML,所以你可以将几个组件放在一起以创建更复杂的 HTML 页面。 这是 React 提供的组件架构的关键优势之一。 它允许用许多独立的组件组合成 UI。 这使得构建和维护复杂的用户界面变得更加容易。 定义 React 组件的另一种方法是使用 ES6 的 在以下示例中, 这将创建一个 ES6 类 因此, 另请注意, 构造函数是使用 最佳做法是在组件的 现在来看看如何组合多个 React 组件。 想象一下,现在正在构建一个应用程序,并创建了三个组件: 要将这些组件组合在一起,可以创建一个 要在 React 组件中渲染一个子组件,需要在 JSX 中包含作为自定义 HTML 标签编写的组件名称。 例如,在 当 React 遇到一个自定义 HTML 标签引用另一个组件的时(如本例所示,组件名称包含在 这可以说明 React 中最重要的主题之一是 可以在类组件的 可以在组件的整个生命周期内访问 定义了组件的初始 state 之后,就可以在要渲染的 UI 中显示它。 如果组件是有状态的,它将始终可以访问 如果想在 render 方法的 注意,如果组件是有状态的,其它组件并不知道它的 前面的挑战涵盖了组件的 React 要求永远不要直接修改 除了设置和更新 一种常见的方法是在构造函数中显式地绑定 然后,当在类方法中调用像 有时可能在更新状态的时候想知道上一个状态是什么。 但是状态更新是异步的,这意味着 React 可能会把多个 正确的做法是,给 如果只需要 注意一定要把 object 放在括号里,否则 JavaScript 会认为这只是代码片段。 练习: 还记不记得在之前的示例中使用 ReactDOM API 将 JSX 元素渲染到 DOM, 这与渲染 React 组件的过程十分相似。如果不调用 ReactDOM API,编写的任何 React 代码都不会渲染到 DOM。 复习一下语法: 传递到 此语法用于 ES6 class 组件和函数组件都可以。 示例: 在后台引入了 将两个组件渲染为 现在是时候看看 React 中的另一个常见特性 props 了。 在 React 中,可以将属性传递给子组件。 假设有一个 可以通过以下方式给 可以把创建的 React 支持的自定义 HTML 属性传递给组件, 在上面的例子里,将创建的属性 由于 调用 示例: 代码编辑器中有 从 然后访问 请注意,要将 The current date is: {props.date} 上一个示例演示了如何将来自父组件的信息作为 接下来着眼于如何将数组作为 要将数组传递给 JSX 元素,必须将其视为 JavaScript 并用花括号括起来。 这样,子组件就可以访问数组属性 {props.colors.join(', ')} 这将把所有 green, blue, red 稍后,将了解在 React 中渲染数组数据的其他常用方法。 练习: {props.tasks.join(',')} React 还有一个设置默认 props 的选项。 可以将默认 props 作为组件本身的属性分配给组件,React 会在必要时分配默认 prop。 如果没有显式的提供任何值,这允许指定 prop 值应该是什么。 例如,如果声明 即定义一个 location 属性,并且其值在没有另行定义的情况下被设置为字符串 在 React 中,设置默认的 props 是一个很有用的特性, 直接设置组件的 prop 值即可覆盖默认 props。 React 提供了有用的类型检查特性,以验证组件是否接收了正确类型的 props。 例如,应用程序调用 API 来检索数据是否是数组,然后将数据作为 prop 传递给组件。 可以在组件上设置 当提前知道 prop 的类型时,最佳实践是设置其 在上面的示例中, 在 7 种 JavaScript 原语类型中, 例如,你可以检查 prop 是否为 React 元素。 请参阅文档以获取所有选项。 注意:在 React v15.5.0 中, 练习: 为 前几项挑战涵盖了将 props 传递给子组件的基本方法。 但是,倘若接收 prop 的子组件不是无状态函数组件,而是一个 ES6 类组件又当如何呢? ES6 类组件访问 props 的方法略有不同。 任何时候,如果要引用类组件本身,可以使用 练习: 在父组件 Your temporary password is: {this.props.tempPassword} 5.7 将 State 作为 Props 传递给子组件 在之前的挑战中,看到了很多将 props 传递给子 JSX 元素和子 React 组件的例子。 你可能想知道那些 props 是从哪里来的。 一个常见的模式是:有状态组件中包含对应用程序很重要的 例如,有一个 这个模式说明了 React 中的一些重要范例。 第一个是单向数据流, state 沿着应用程序组件树的一个方向流动,从有状态父组件到子组件, 子组件只接收它们需要的 state 数据。 第二,复杂的有状态应用程序可以分解成几个,或者可能是一个单一的有状态组件。 其余组件只是从父组件简单的接收 state 作为 props,并从该 state 渲染 UI。 它开始创建一种分离,在这种分离中,state 管理在代码的一部分中处理,而 UI 渲染在另一部分中处理。 将 state 逻辑与 UI 逻辑分离是 React 的关键原则之一。 当它被正确使用时,它使得复杂的、有状态的应用程序的设计变得更容易管理。 可以将 代码编辑器中列出了三个组件。 接下来,将 {this.props.input} 编写这些方法,使计数器值在单击相应按钮时增加或减少 1。 另外,创建一个 注意: 确保没有修改按钮的 应用程序可能在 代码编辑器具有一个名为 首先,创建一个名为 在 在输入框中键入时,文本由 最后,不要忘记在构造函数中添加必要的绑定。 {this.state.input} 我们增加了一个提交表单的按钮。 可以看到它的 注意: 还必须在提交处理程序中调用 最后,在 React 组件有几种特殊方法,可以在组件生命周期的特定点执行操作。 这些称为生命周期方法或生命周期钩子,允许在特定时间点捕获组件。 这可以在渲染之前、更新之前、接收 props 之前、卸载之前等等。 以下是一些主要生命周期方法的列表: 注意: 当组件被挂载到 DOM 时, 某些时候,大多数 web 开发人员需要调用 API 接口来获取数据。 如果正在使用 React,知道在哪里执行这个动作是很重要的。 React 的最佳实践是在生命周期方法 之前已经接触了一些合成事件处理程序,如 在 然后,在 到目前为止,如果任何组件接收到新的 这种方法是优化性能的有效方法。 例如,默认行为是,当组件接收到新的 将 还有其他复杂的概念可以为 React 代码增加强大的功能。 但是,你可能会想知道更简单的问题,比如:如何对在 React 中创建的 JSX 元素添加样式。 你可能知道,由于将 class 应用于 JSX 元素的方式与 HTML 中的使用并不完全相同。 如果从样式表导入样式,它就没有太大的不同。 使用 将内联样式应用于 JSX 元素,类似于在 HTML 中的操作方式,但有一些 JSX 差异。 以下是 HTML 中内联样式的示例: JSX 元素使用 注意到如何驼峰拼写 在代码编辑器中给 请注意,可以选择将字体大小设置为数字,省略单位 markup 如果 再来看看前面的示例, 在继续使用动态渲染技术之前,还有最后一种方法可以渲染想要的东西,它使用内置的 JavaScript 条件:三元运算符。 三元运算符经常被用作 JavaScript 中 代码编辑器在 一旦组件将信息渲染给页面,用户应该有一种方法与之交互。 在组件的 到目前为止,已经看到了如何使用 在这个挑战中,将设置一个子组件来根据 props 做出渲染决定。 可以使用三元运算符,但是可以看到过去几个挑战中涵盖的其他几个概念在这种情况下可能同样有用。 代码编辑器有两个部分为你定义的组件:一个名为 首先,需要一个简单的表达式,每次运行时都会随机返回一个不同的值。 可以使用 现在了一个表达式,可以使用该表达式在代码中做出随机决策。 接下来,需要实现此功能。 将 {'Turn: ' + this.state.counter} 条件渲染很有用,但是可能需要组件来渲染未知数量的元素。 通常在响应式编程中,程序员在应用程序运行时之前无法知道其 state,因为这在很大程度上取决于用户与该程序的交互。 程序员需要提前编写代码来正确处理未知状态。 在 React 中使用 例如,创建一个简单的“To Do List”应用程序。 作为程序员,你无法知道用户可能在其列表中有多少项。 需要设置组件,以便在使用该程序的人决定今天今日待办事项之前动态渲染正确数量的列表元素。 代码编辑器完成了 在 注意: 像这样的映射操作创建的所有兄弟子元素都需要提供唯一的 上一个挑战展示了如何使用 注意: 键只需要在兄弟元素之间是唯一的,它们不需要在应用程序中是全局唯一的。 代码编辑器有一个数组,它包含一些前端框架和一个名为 通常,希望使 key 能唯一标识要渲染的元素。 数组索引可以是最后的选择,但通常你应该尝试使用唯一标识。 在代码编辑器中, 到目前为止,已经能够在客户端上渲染 React 组件, 一般来说我们都是这么做的。 然而,在一些用例中,需要在服务器上渲染一个 React 组件。 由于 React 是一个 JavaScript 视图库,所以通常使用 Node 让 JavaScript 运行在服务器上。 事实上,React 提供了一个可用于此目的的 有两个关键原因可以解释为什么服务器上的渲染可能会在真实世界的应用程序中使用。 首先,如果不这样做,当 React 应用程序最初加载到浏览器时,它将包含一个代码量很少的 HTML 文件和一大堆 JavaScript。 这对于搜索引擎来说可能不太理想,因为它们试图为网页内容生成索引,以便人们可以找到这个应用。 如果在服务器上渲染初始 HTML 标记并将其发送到客户端,则初始页面加载的内容包含搜索引擎可以抓取的所有页面标记。 其次,这创造了更快的初始页面加载体验,因为渲染的 HTML 代码量要比整个应用程序的 JavaScript 代码小。 React 仍然能够识别你的应用并在初始加载后进行管理。 或者
。 不同之处在于,在第一个语法版本中,无法在
中包含任何内容。 在后面的挑战中你会发现,这种语法在渲染 React 组件时非常有用。(往下看两节再回来看这一段就明白很多。)
3 创建组件
3.1 JS函数创建组件----无状态组件
null
的 JavaScript 函数。 需要注意的一点是,React 要求你的函数名以大写字母开头。 下面是一个无状态功能组件的示例,该组件在 JSX 中分配一个 HTML 的 class:const DemoComponent = function() {
return (
);
};
customClass
的 CSS class。
3.2 class创建组件
class
语法。Kitten
扩展了React.Component
:class Kitten extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
Hi
);
}
}
Kitten
,它扩展了 React.Component
类。Kitten
类现在可以访问许多有用的 React 功能,例如本地状态和生命周期钩子。
Kitten
类中定义了一个调用 super()
方法的 constructor
。 它使用 super()
调用父类的构造函数,即本例中的 React.Component
。class
关键字创建的特殊方法,它在实例初始化之前调用。constructor
里调用 super
,并将 props
传递给它们, 这样可以保证组件能够正确地初始化。 目前为止 ,需要知道这些代码是必要的。 很快会了解到到构造函数的其他用途以及 props
。
3.3 用组合的方式创建一个 React 组件
Navbar
、Dashboard
和 Footer
。App
父组件,将这三个组件分别渲染成为子组件。render
方法中,可以这样编写:return (
< />
中),它在自定义标签的位置渲染该组件的标签。App
组件和 Navbar
、Dashboard
以及 Footer
之间的父子关系。
3.4 创建一个有状态的组件
state
。 state 包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。 应用程序能够响应 state 的变更,并在必要时显示更新后的 UI。 React 为现代 Web 应用程序的状态管理提供了一个很好的解决方案。constructor
上声明 state
属性来在 React 组件中创建 state, 它在创建时使用 state
初始化组件。 state
属性必须设置为 JavaScript object
(对象)。 声明如下:this.state = {
}
state
对象, 可以更新它、在 UI 中渲染它,也可以将其作为 props 传递给子组件。 state
对象的使用可以很简单,亦可以很复杂,就看你怎么用了。 请注意,必须通过扩展 React.Component
来创建类组件,以便像这样创建 state
。render()
方法中 state
的数据。 就可以使用 this.state
访问数据。return
中访问 state 值,必须把这个值用花括号括起来。state
是 React 组件中最强大的特性之一, 它可以跟踪应用程序中的重要数据,并根据数据的变化渲染 UI。 如果数据发生变化,UI 也会随之改变。 React 使用所谓的虚拟 DOM 来跟踪幕后的变化。 当 state 数据更新时,它会使用该数据触发组件的重新渲染 -- 包括接收 prop 数据的子组件。 React 只在必要的时候更新实际的 DOM, 这意味着你不必担心 DOM 的变更, 只需声明 UI 的外观即可。state
。 它的 state
是完全封装的,或者是局限于组件本身的,除非你将 state 数据作为 props
传递给子组件。 封装 state
的概念非常重要,因为它允许编写特定的逻辑,然后将该逻辑包含并隔离在代码中的某个位置。还有另一种方法可以访问组件中的 state
。 在 render()
方法中,在 return
语句之前,可以直接编写 JavaScript。 例如,可以声明函数、从 state
或 props
中访问数据、对此数据执行计算等。 然后,可以将任何数据赋值给 return
语句中可以访问的变量。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// 修改这行下面的代码
const name=this.state.name;
// 修改这行上面的代码
return (
{name}
{ /* 修改这行上面的代码 */ }
3.4.1 用 this.setState 设置状态
state
以及如何在 constructor
中初始化 state。 还有一种方法可以更改组件的 state
, React 提供了 setState
方法来更新组件的 state
。 在组件类中调用 setState
方法如下所示:this.setState()
,传入键值对的对象, 其中键是 state 属性,值是更新后的 state 数据。 例如,如果我们在 state 中存储 username
,并想要更新它,代码如下所示:this.setState({
username: 'Lewis'
});
state
,而是在 state 发生改变时始终使用 this.setState()
。 此外,应该注意,React 可以批量处理多个 state 更新以提高性能。 这意味着通过 setState
方法进行的 state 更新可以是异步的。
3.4.2 将 this 绑定到 Class 方法上
state
之外,还可以为组件类定义方法。 类方法通常需要使用 this
关键字,以便它可以访问方法中类的属性(例如 state
和 props
)。 有几种方法可以让类方法访问 this
。this
,这样当组件初始化时,this
就会绑定到类方法。 你可能已经注意到上一个挑战在构造函数中的 handleClick
方法使用了
this.handleClick = this.handleClick.bind(this)
this.setState()
这样的函数时,this
指的是这个类,而不是 undefined
。
3.4.3 使用 State 切换元素
setState()
集中在一起批量更新。 所以计算下一个值时 this.state
或者 this.props
不能作为当前值。 所以最好不要写如下的代码:this.setState({
counter: this.state.counter + this.props.increment
});
setState
传入一个函数,这个函数可以访问 state 和 props。 给 setState
传入函数可以保证 state 和 props 是正确的值。 代码可以重写为这样:this.setState((state, props) => ({
counter: state.counter + props.increment
}));
state
,那么用下面没有 props
的格式也是可以的:this.setState(state => ({
counter: state.counter + 1
}));
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
visibility: false
};
// 修改这行下面的代码
this.toggleVisibility=this.toggleVisibility.bind(this);
// 修改这行上面的代码
}
// 修改这行下面的代码
toggleVisibility() {
this.setState(state=> ({
visibility:!(state.visibility)
}));
}
// 修改这行上面的代码
render() {
if (this.state.visibility) {
return (
Now you see me!
4 将 class 组件渲染到 DOM 树
ReactDOM.render(componentToRender, targetNode)
。 第一个参数是要渲染的 React 组件。 第二个参数是要在其中渲染该组件的 DOM 节点。ReactDOM.render()
的React 组件与 JSX 元素略有不同。 对于 JSX 元素,传入的是要渲染的元素的名称。 但是,对于 React 组件,需要使用与渲染嵌套组件相同的语法,例如
ReactDOM.render(
Fruits
和 Vegetables
组件。TypesOfFood
组件的子组件,然后将 TypesOfFood
渲染到 DOM 节点, 在这个挑战中,请渲染到 id='challenge-node'
的 div
中。class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
Types of Food:
{/* 修改这行下面的代码 */}
5 传参
5.1 将 Props 传递给无状态函数组件
App
组件,该组件渲染了一个名为 Welcome
的子组件,它是一个无状态函数组件。Welcome
传递一个 user
属性:user
传递给组件 Welcome
。Welcome
是一个无状态函数组件,它可以像这样访问该值:const Welcome = (props) =>
Hello, {props.user}!
props
这个值是常见做法,当处理无状态函数组件时,基本上可以将其视为返回 JSX 的函数的参数。 这样,你就可以在函数体中访问该值。 但对于类组件,访问方式会略有不同。Calendar
和 CurrentDate
组件。Calendar
组件渲染 CurrentDate
时,从 JavaScript 的 Date
对象分配当前日期,并将其作为 date
属性传入。CurrentDate
组件的 prop
,并在 p
标签中显示其值。prop
的值视为 JavaScript,必须将它们括在花括号中,例如date={Date()}
。const CurrentDate = (props) => {
return (
What date is it?
{ /* 修改这行下面的代码 */ }
5.2 传递一个数组作为 Props
props
传递给子组件。props
传递。colors
。访问属性时可以使用 join()
等数组方法。 const ChildComponent = (props) =>
colors
数组项连接成一个逗号分隔的字符串并生成:
List
和 ToDo
组件。ToDo
组件中渲染每个 List
时,传入 tasks
属性并将其分配给待办任务数组,例如 ["walk dog", "workout"]
。List
组件中的 tasks
数组,在p
元素中显示其值。join(", ")
把 props.tasks
数组作为逗号分隔列表显示在 p
元素中。const List = (props) => {
{ /* 修改这行下面的代码 */ }
return
To Do Lists
Today
{ /* 修改这行下面的代码 */ }
Tomorrow
{ /* 修改这行上面的代码 */ }
5.3 使用默认的 Props
MyComponent.defaultProps = { location: 'San Francisco' }
San Francisco
。 如果 props 未定义,则 React 会分配默认 props,但如果你将 null
作为 prop 的值,它将保持 null
。
5.4 覆盖默认的 Props
5.5 使用 PropTypes 来定义 Props 的类型
propTypes
,以要求数据的类型为 array
。 当数据是任何其它类型时,都会抛出警告。propTypes
。 可以为组件定义 propTypes
属性,方法与定义 defaultProps
相同。 这样做将检查给定键的 prop 是否是给定类型。 这里有一个示例,表示名为 handleClick
的 prop 应为 function
类型:MyComponent.propTypes = { handleClick: PropTypes.func.isRequired }
PropTypes.func
部分检查 handleClick
是否为函数。 添加isRequired
,告诉 React handleClick
是该组件的必需属性。 如果没有那个属性,将出现警告。 还要注意 func
代表 function
。function
和 boolean
(写为 bool
)是唯一使用异常拼写的两种类型。 除了原始类型,还有其他类型可用。PropTypes
可以从 React 中单独引入,例如:import PropTypes from 'prop-types';
。Items
组件定义 propTypes
,以要求 quantity
作为 prop,并验证它是否为 number
类型。const Items = (props) => {
return
Current Quantity of Items in Cart: {props.quantity}
};
// 修改这行下面的代码
Items.propTypes={quantity:PropTypes.number.isRequired }
// 修改这行上面的代码
Items.defaultProps = {
quantity: 0
};
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
return
5.6 使用 this.props 访问 Props
this
关键字。 要访问类组件中的 props,需要在在访问它的代码前面添加 this
。 例如,如果 ES6 类组件有一个名为 data
的 prop,可以在 JSX 中这样写:{this.props.data}
。ResetPassword
中渲染 ReturnTempPassword
组件的一个实例。 在这里,为 ReturnTempPassword
提供一个 tempPassword
prop,并赋值一个长度至少为 8 个字符的字符串。 在子组件 ReturnTempPassword
中,访问 strong
标签中的 tempPassword
prop,以确保用户看到临时密码。class ReturnTempPassword extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
Reset Password
We've generated a new temporary password for you.
Please reset this password from your account settings ASAP.
{ /* 修改这行下面的代码 密码是随意写的*/ }
state
,然后用它渲染子组件。 如果想让这些组件能够访问该 state
的某些部分,就把这些部分作为 props 传入。App
组件可以渲染 Navbar
以及其他组件。 App
里的 state
包含大量用户信息,但 Navbar
只需要访问用户的用户名,以便显示它。 将该 state
作为 prop 传递给Navbar
组件。MyApp
组件是有状态的,并将 Navbar
组件渲染为子组件。 将 state
的 name
属性向下传递给子组件,然后在 h1
中显示该 name
,h1 是 Navbar
render方法的一部分。 name
应该显示在文本 Hello, my name is:
后面。class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
Hello, my name is:{this.props.name}
{/* 修改这行上面的代码 */}
5.8 传递回调作为 Props
state
作为 props 传递给子组件,但不仅限于传递数据。 也可以将函数或在 React 组件中定义的任何方法传递给子组件。 这就是子组件与父组件交互的方式。 可以把方法像普通 prop 一样传递给子组件, 它会被分配一个名字,可以在子组件中的 this.props
下访问该方法的名字。MyApp
是父组件,GetInput
和RenderInput
是它将要渲染的子组件。 将 GetInput
组件添加到 MyApp
的 render 方法,然后将 MyApp
的 state
中的 inputValue
传入名为 input
的 prop。 还要创建一个名为 handleChange
的 prop,并将输入处理程序 handleChange
传递给它。RenderInput
添加到 MyApp
中的 render 方法中,然后创建一个名为 input
的 prop,并将 state
中的 inputValue
传递给它。 完成后,可以在 GetInput
组件中的 input
字段中键入内容,然后该组件通过 props 调用其父组件中的处理函数方法。 这将更新处于父组件 state
中的 input,该 input 将作为 props 传递给两个子组件。 观察数据如何在组件之间流动,以及单一数据源如何保持父组件state
。 诚然,这个示例有点做作,但是应该能用来说明数据和回调是如何在 React 组件之间传递的。class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
Get Input:
Input Render:
6 小例子
6.1 一个简单的计数器
Counter
组件跟踪 state
中的 count
值。 有两个按钮分别调用 increment()
和 decrement()
方法。reset()
方法,当单击 reset 按钮时,把计数设置为 0。className
。 另外,请记住在构造函数中为新创建的方法添加必要的绑定。class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
// 修改这行下面的代码
this.increment=this.increment.bind(this);
this.decrement=this.decrement.bind(this);
this.reset=this.reset.bind(this);
// 修改这行上面的代码
}
// 修改这行下面的代码
increment() {
this.setState(state=>({
count:state.count+1
}));
}
decrement() {
this.setState(state=>({
count:state.count-1
}));
}
reset() {
this.setState(state=>({
count:0
}));
}
// 修改这行上面的代码
render() {
return (
Current Count: {this.state.count}
6.2 创建一个可以控制的输入框
state
和渲染的 UI 之间有更复杂的交互。 例如,用于文本输入的表单控件元素(如 input
和 textarea
)在用户键入时在 DOM 中维护自己的 state。 通过 React,可以将这种可变 state 转移到 React 组件的 state
中。 用户的输入变成了应用程序 state
的一部分,因此 React 控制该输入字段的值。 通常,如果 React 组件具有用户可以键入的输入字段,那么它将是一个受控的输入表单。ControlledInput
的组件框架,用于创建受控的 input
元素。 组件的 state
已经被包含空字符串的 input
属性初始化。 此值表示用户在 input
字段中键入的文本。handleChange()
的方法,该方法具有一个名为 event
的参数。 方法被调用时,它接收一个 event
对象,该对象包含一个来自 input
元素的字符串文本。 可以使用方法内的 event.target.value
来访问这个字符串。 用这个新字符串更新组件的state
的input
属性。render
方法中的 h4
标签之上创建 input
元素。 添加一个 value
属性,使其等于组件 state
的 input
属性。 然后将 onChange()
事件处理程序设置到 handleChange()
方法中。handleChange()
方法处理,文本被设置为本地 state
中的 input
属性,并渲染在页面上的 input
框中。 组件 state
是输入数据的唯一真实来源。class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// 修改这行下面的代码
this.handleChange=this.handleChange.bind(this);
// 修改这行上面的代码
}
// 修改这行下面的代码
handleChange(event) {
this.setState({
input:event.target.value
})
}
// 修改这行上面的代码
render() {
return (
Controlled Input:
6.3 创建一个可以控制的表单
MyForm
组件中是一个带有提交处理程序的空 form
元素, 提交处理程序将在提交表单时被调用。type
被设置为 submit
,表明它是控制表单提交的按钮。 在 form
中添加 input
元素,并像上个挑战一样设置其 value
和 onChange()
属性。 然后,应该完成 handleSubmit
方法,以便将组件 state 属性 submit
设置为本地 state
下的当前输入值。event.preventDefault()
,以防止将会刷新网页的默认的表单提交行为。 为了便于学员操作,默认行为在这里被禁用,以防止重置挑战的代码。form
元素之后创建一个 h1
标签,该标签从组件的 state
渲染 submit
的值。 然后,可以在表单中键入任何内容,然后单击按钮(或按 enter 键),输入会渲染到页面上。class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
// 修改这行下面的代码
event.preventDefault();
this.setState({
submit:this.state.input
});
// 修改这行上面的代码
}
render() {
return (
{this.state.submit}
{/* 修改这行上面的代码 */}
7 其他
7.1 使用生命周期方法:componentWillMount
componentWillMount()
componentDidMount()
shouldComponentUpdate()
componentDidUpdate()
componentWillUnmount()
接下来的几节课将讲述这些生命周期方法的一些基本用例。componentWillMount
生命周期方法会在版本 16.X 废弃在版本 17 移除。componentWillMount()
方法在 render()
方法之前被调用。 在componentWillMount()
中将一些内容记录到控制台 -- 可能需要打开浏览器控制台以查看输出。
7.2 使用生命周期方法:componentDidMount
componentDidMount()
中对服务器进行 API 调用或任何其它调用。 将组件装载到 DOM 后会调用此方法。 此处对 setState()
的任何调用都将触发组件的重新渲染。 在此方法中调用 API 并用 API 返回的数据设置 state 时,一旦收到数据,它将自动触发更新。componentDidMount()
中有一个模拟 API 调用。 它在 2.5 秒后设置 state,以模拟调用服务器检索数据。 本示例请求站点的当前活动用户总数。 在 render 方法中,把 activeUsers
渲染到文字 Active Users:
后的 h1
标签中。 观看预览中发生的事情,随意更改超时时间以查看不同的效果。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeUsers: null
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
activeUsers: 1273
});
}, 2500);
}
render() {
return (
Active Users: {this.state.activeUsers}
{/* 修改这行上面的代码 */}
7.3 添加事件侦听器
componentDidMount()
方法也是添加特定功能所需的任何事件监听器的最佳位置。 React 提供了一个合成事件系统,它封装了浏览器中的事件系统。 这意味着,不管用户用的是什么浏览器,合成事件系统的行为都完全相同 -- 即使不同浏览器之间的本地事件的行为可能不同。onClick()
。 React 的合成事件系统非常适合用于在 DOM 元素上管理的大多数交互。 但是,如果要将事件处理程序附加到 document 或 window 对象,则必须直接执行此操作。componentDidMount()
方法中为 keydown
事件添加事件监听器,并让这些事件触发回调 handleKeyPress()
。 可以使用 document.addEventListener()
,它将事件(用引号括起来)作为第一个参数,将回调作为第二个参数。componentWillUnmount()
中移除相同的事件监听器。 可以把相同的参数传递给 document.removeEventListener()
。 在卸载和销毁 React 组件之前,最好在这个生命周期方法中对它们进行清理。 移除事件监听器就是这样一个清理操作的例子。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
// 修改这行下面的代码
componentDidMount() {
document.addEventListener('keydown',this.handleKeyPress);
}
componentWillUnmount() {
document.removeEventListener('keydown',this.handleKeyPress);
}
// 修改这行上面的代码
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
{this.state.message}
7.4 使用 shouldComponentUpdate 优化重新渲染
state
或新的 props
,它会重新渲染自己及其所有子组件。 这通常是好的。 但是 React 提供了一种生命周期方法,当子组件接收到新的 state
或 props
时,可以调用该方法,并特别声明组件是否应该更新。 这个方法就是 shouldComponentUpdate()
,它将 nextProps
和 nextState
作为参数。props
时,即使 props
没有改变,它也会重新渲染。 可以通过使用 shouldComponentUpdate()
比较 props
来防止这种情况发生。 该方法必须返回一个 boolean
(布尔值),该值告诉 React 是否更新组件。 可以比较当前的 props(this.props
)和下一个 props(nextProps
),以确定你是否需要更新,并相应地返回 true
或 false
。shouldComponentUpdate()
方法添加到名为 OnlyEvens
的组件中。 目前,该方法返回 true
,因此每次收到新的 props
时,OnlyEvens
都会重新渲染。 修改该方法,以便 OnlyEvens
仅在其新 props 的 value
为偶数时更新。 单击 Add
按钮,在触发其他生命周期钩子时,在浏览器控制台中查看事件的顺序。class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// 修改这行下面的代码
if(nextProps.value % 2 == 0) {
return true;
}else {
return false;
}
// 修改这行上面的代码
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return
{this.props.value}
;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
7.5 内联样式
className
属性将 class 应用于 JSX 元素,并将样式应用于样式表中的 class。 另一种选择是使用内联样式,这在 ReactJS 开发中非常常见。style
属性,但是鉴于 JSX 的编译方式,不能将值设置为 string
(字符串)。 相反,你应该将其设置为等于JavaScript object
。 如下所示:fontSize
属性了吗? 这是因为 React 不接受样式对象中的 kebab-case 键。 React 将在 HTML 中为应用正确的属性名称。div
添加一个 style
属性,将文本颜色设置为红色,字体大小设置为 72px
。px
,或者将其写为 72px
。
7.6 使用 && 获得更简洁的条件
if/else
语句在上一次挑战中是有效的,但是有一种更简洁的方法可以达到同样的结果。 假设正在跟踪组件中的几个条件,并且希望根据这些条件中的每一个来渲染不同的元素。 如果你写了很多 else if
语句来返回稍微不同的 UI,你可能会写很多重复代码,这就留下了出错的空间。 相反,你可以使用 &&
逻辑运算符以更简洁的方式执行条件逻辑。 这是完全可行的,因为你希望检查条件是否为 true
。如果是,则返回一些标记。 下面是一个示例:{condition &&
condition
为 true
,则返回标记。 如果条件为 false
,则在评估 condition
后操作将立即返回 false
,并且不返回任何内容。 可以将这些语句直接包含在 JSX 中,并通过在每个条件后面写 &&
来将多个条件串在一起。 这允许你在 render()
方法中处理更复杂的条件逻辑,而无需重复大量代码。h1
还是在 display
为 true
时渲染,但使用 &&
逻辑运算符代替 if/else
语句。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// 修改这行下面的代码
return (
Displayed!
}
7.7 使用三元表达式进行条件渲染
if/else
语句的缩写。 它们不像传统的 if/else
语句那样强大,但是在 React 开发人员中非常流行, 原因之一就是 JSX 的编译原理,if/else
语句不能直接插入到 JSX 代码中。 可能你在前几个挑战就注意到了这一点——当需要 if/else
语句时,它总是在 return
语句的外面。 如果想在 JSX 中实现条件逻辑,三元表达式是一个很好的选择。 回想一下,三元运算符有三个部分,但是可以将多个三元表达式组合在一起。 以下是基本语法:condition ? expressionIfTrue : expressionIfFalse;
CheckUserAge
组件的 render()
方法中定义了三个常量, 它们分别是 buttonOne
、buttonTwo
和 buttonThree
。 每个都分配了一个表示按钮元素的简单 JSX 表达式。 首先,使用 input
和 userAge
初始化 CheckUserAge
的 state,并将其值设置为空字符串。return
语句中,设置一个实现以下逻辑的三元表达式:当页面首次加载时,将提交按钮 buttonOne
渲染到页面。 然后,当用户输入年龄并点击该按钮时,根据年龄渲染不同的按钮。 如果用户输入的数字小于18
,则渲染buttonThree
。 如果用户输入的数字大于或等于18
,则渲染buttonTwo
。const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
// 修改这行下面的代码
this.state={
input:'',
userAge:''
};
// 修改这行上面的代码
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = ;
const buttonTwo = ;
const buttonThree = ;
return (
Enter Your Age to Continue
{/* 修改这行下面的代码 */}
{this.state.userAge===''? buttonOne : this.state.userAge <18 ? buttonThree : buttonTwo}
{/* 修改这行上面的代码 */}
7.8 根据 Props 有条件地渲染
if/else
、&&
和三元运算符(condition ? expressionIfTrue : expressionIfFalse
)对渲染什么和何时渲染做出有条件的判定。 然而,还有一个重要的话题需要讨论,将这些概念中的任何一个或所有概念与另一个强大的 React 功能 props 结合起来。 使用 props 有条件地渲染代码在 React 开发人员中很常见——也就是说:他们使用给定 prop 的值来自动决定渲染什么。GameOfChance
的父组件和一个名为 Results
的子组件。 它们被用来创建一个简单的游戏,用户按下按钮来看它们是赢还是输。Math.random()
。 每次调用此方法时,此方法返回 0
(包括)和 1
(不包括)之间的值。 因此,对于50/50的几率,请在表达式中使用 Math.random() >= .5
。 从统计学上讲,这个表达式有 50% 的几率返回 true
,另外 50% 返回 false
。 在第 render 方法里,用此表达式替换 null
以完成变量声明。Results
组件渲染为 GameOfChance
的子 组件,并将 expression
作为名为 fiftyFifty
的 prop 传入 。 在 Results
组件中,编写一个三元表达式来渲染 h1
元素的文本。GameOfChance
传来的 prop fiftyFifty
来决定渲染文本 You Win!
还是 You Lose!
。 最后,确保 handleClick()
方法正确计算每个回合,以便用户知道他们玩过多少次。 这也可以让用户知道组件实际上已经更新,以防他们连续赢两次或输两次时自己不知道。class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
{/* 修改这行下面的代码 */}
return
{this.props.fiftyFifty ? 'you win!':'You Lose!'}
;
{/* 修改这行上面的代码 */}
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => {
// 完成 return 语句
return {
counter: this.state.counter+1
}
});
}
render() {
const expression = Math.random() >= 0.5 ? true : false; // 修改这一行
return (
7.9 使用 Array.map() 动态渲染元素
Array.map()
阐明了这个概念。MyToDoList
组件的大部分设置。 如果完成了受控表单挑战,这些代码中的一些应该看起来很熟悉。 你会注意到一个 textarea
和一个 button
,以及一些跟踪它们状态的方法,但是页面当前还没有任何东西被渲染。constructor
中,创建一个 this.state
对象并定义两个 state:userInput
应该初始化为空字符串,toDoList
应该初始化为空数组。 接下来,删除 items
变量旁边 render()
方法中的注释。 取而代之的是,将存储在组件内部 state 中的 toDoList
数组一一遍历并相应的动态呈现 li
元素中。 尝试在 textarea
中输入 eat, code, sleep, repeat
,然后点击按钮,看看会发生什么。key
属性。 别担心,这是下一个挑战的主题。const textAreaStyles = {
width: 235,
margin: 5
};
class MyToDoList extends React.Component {
constructor(props) {
super(props);
// 修改这行下面的代码
this.state = {
userInput:'',
toDoList:[]
};
// 修改这行上面的代码
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit() {
const itemsArray = this.state.userInput.split(',');
this.setState({
toDoList: itemsArray
});
}
handleChange(e) {
this.setState({
userInput: e.target.value
});
}
render() {
const items = this.state.toDoList.map(i=>
My "To Do" List:
{items}
7.10 给同级元素一个唯一的键属性
map
方法根据用户输入动态渲染多个元素。 然而,这个例子中缺少一个重要的部分。 创建元素数组时,每个元素都需要一个设置为唯一值的 key
属性。 React 使用这些键来跟踪哪些项目被添加、更改或删除。 这有助于在以任何方式修改列表时提高重新渲染过程的效率。Frameworks()
的无状态函数组件。 Frameworks()
需要将数组映射到无序列表,就像上一个挑战一样。 完成 map
回调,为 frontEndFrameworks
数组中的每个框架返回一个 li
元素。 这次,确保给每个 li
的 key
属性设置一个唯一的值。 li
元素还应该包含来自 frontEndFrameworks
的文本。const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = frontEndFrameworks.map(i=>
Popular Front End JavaScript Frameworks
{renderFrameworks}
7.11 使用 Array.Filter() 动态过滤数组
map
数组方法是一个强大的工具,在使用 React 时经常使用。 与 map
相关的另一种方法是 filter
,它根据条件过滤数组的内容,然后返回一个新数组。 例如,如果有一个 users 数组,每个数组元素都有一个可以设置为 true
或 false
的 online
属性,可以这样只过滤那些在线的用户:let onlineUsers = users.filter(user => user.online);
MyComponent
的 state
被初始化为一个用户数组。 有些用户在线,有些则没有。 过滤数组,以便只查看在线用户。 要执行此操作,请首先使用 filter
返回仅包含 online
属性为 true
的用户的新数组。 然后,在 renderOnline
变量中,映射已过滤的数组,并为包含其 username
文本的每个用户返回 li
元素。 确保包含一个唯一的 key
,就像上一个挑战一样。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [
{
username: 'Jeff',
online: true
},
{
username: 'Alan',
online: false
},
{
username: 'Mary',
online: true
},
{
username: 'Jim',
online: false
},
{
username: 'Sara',
online: true
},
{
username: 'Laura',
online: true
}
]
};
}
render() {
const usersOnline = this.state.users.filter(user=>user.online===true); // 修改这一行
const renderOnline = usersOnline.map(i=>
Current Online Users:
{renderOnline}
7.12 用 renderToString 在服务器上渲染 React
renderToString()
方法。renderToString()
方法由 ReactDOMServer
提供,在这里已为你定义成全局变量。 这个方法接收一个 React 元素作为参数。 用它来把 App
渲染成字符串。class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return
}
};
// change code below this line
ReactDOMServer.renderToString(