一、create-react-app
全局安装create-react-app
$ npm install -g create-react-app
1
创建一个项目
$ create-react-app your-app 注意命名方式
Creating a new React app in /dir/your-app.
Installing packages. This might take a couple of minutes. 安装过程较慢,
Installing react, react-dom, and react-scripts...
1
2
3
4
5
6
如果不想全局安装,可以直接使用npx
$ npx create-react-app your-app 也可以实现相同的效果
1
这需要等待一段时间,这个过程实际上会安装三个东西
react: react的顶级库
react-dom: 因为react有很多的运行环境,比如app端的react-native, 我们要在web上运行就使用react-dom
react-scripts: 包含运行和打包react应用程序的所有脚本及配置
出现下面的界面,表示创建项目成功:
Success! Created your-app at /dir/your-app
Inside that directory, you can run several commands:
npm start
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Starts the test runner.
npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd your-app
npm start
Happy hacking!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
根据上面的提示,通过cd your-app命令进入目录并运行npm start即可运行项目。
生成项目的目录结构如下:
├── README.md 使用方法的文档
├── node_modules 所有的依赖安装的目录
├── package-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。
├── package.json
├── public 静态公共目录
└── src 开发用的源代码目录
1
2
3
4
5
6
常见问题:
npm安装失败
切换为npm镜像为淘宝镜像
使用yarn,如果本来使用yarn还要失败,还得把yarn的源切换到国内
如果还没有办法解决,请删除node_modules及package-lock.json然后重新执行npm install命令
再不能解决就删除node_modules及package-lock.json的同时清除npm缓存npm cache clean --force之后再执行npm install命令
二、关于React
1、React的起源和发展
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
2、React与传统MVC的关系
轻量级的视图层库!A JavaScript library for building user interfaces
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;React 构建页面 UI 的库。可以简单地理解为,React 将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
3、React高性能的体现:虚拟DOM
React高性能的原理:
在Web开发中我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A-B, B-A,React会认为A变成B,然后又从B变成A UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。
尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
React Fiber:
在react 16之后发布的一种react 核心算法,React Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法。
在之前React中,更新过程是同步的,这可能会导致性能问题。
当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为JavaScript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
4、React的特点和优势
(1) 虚拟DOM
我们以前操作dom的方式是通过document.getElementById()的方式,这样的过程实际上是先去读取html的dom结构,将结构转换成变量,再进行操作。而reactjs定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,性能真实相当的高,和主流MVC框架有本质的区别,并不和dom打交道
(2) 组件系统
react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件 component
那么什么是组件呢?
组件指的就是同时包含了html、css、js、image元素的聚合体
使用react开发的核心就是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式整个颠覆了过去的传统的方式
(3) 单向数据流
其实reactjs的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
(4) JSX 语法
在vue中,我们使用render函数来构建组件的dom结构性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为复杂,此时可以利用jsx语法来在render中创建dom,解决这个问题,但是前提是需要使用工具来编译jsx
三、编写第一个react应用程序
react开发需要引入多个依赖文件:react.js、react-dom.js,分别又有开发版本和生产版本,create-react-app里已经帮我们把这些东西都安装好了。把通过CRA创建的工程目录下的src目录清空,然后在里面重新创建一个index.js. 写入以下代码:
// 从 react 的包当中引入了 React。只要你要写 React.js 组件就必须引入React, 因为react里有一种语法叫JSX,稍后会讲到JSX,要写JSX,就必须引入React
import React from 'react'
// ReactDOM 可以帮助我们把 React 组件渲染到页面上去,没有其它的作用了。它是从 react-dom 中引入的,而不是从 react 引入。
import ReactDOM from 'react-dom'
// ReactDOM里有一个render方法,功能就是把组件渲染并且构造 DOM 树,然后插入到页面上某个特定的元素上
ReactDOM.render(
// 这里就比较奇怪了,它并不是一个字符串,看起来像是纯 HTML 代码写在 JavaScript 代码里面。语法错误吗?这并不是合法的 JavaScript 代码, “在 JavaScript 写的标签的”语法叫 JSX- JavaScript XML。
欢迎进入React的世界
,// 渲染到哪里
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
四、元素与组件
如果代码多了之后,不可能一直在render方法里写,所以就需要把里面的代码提出来,定义一个变量,像这样:
import React from 'react'
import ReactDOM from 'react-dom'
// 这里感觉又不习惯了?这是在用JSX定义一下react元素
const app =
欢迎进入React的世界
ReactDOM.render(
app,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
1、函数式组件
由于元素没有办法传递参数,所以我们就需要把之前定义的变量改为一个方法,让这个方法去return一个元素:
import React from 'react'
import ReactDOM from 'react-dom'
// 特别注意这里的写法,如果要在JSX里写js表达式(只能是表达式,不能流程控制),就需要加 {},包括注释也是一样,并且可以多层嵌套
const app = (props) =>
欢迎进入{props.name}的世界
ReactDOM.render(
app({
name: 'react'
}),
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
这里我们定义的方法实际上也是react定义组件的第一种方式-定义函数式组件,这也是无状态组件。但是这种写法不符合react的jsx的风格,更好的方式是使用以下方式进行改造
import React from 'react'
import ReactDOM from 'react-dom'
const App = (props) =>
欢迎进入{props.name}的世界
ReactDOM.render(
// React组件的调用方式
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
这样一个完整的函数式组件就定义好了。但要注意!注意!注意!组件名必须大写,否则报错。
2、class组件
ES6的加入让JavaScript直接支持使用class来定义一个类,react的第二种创建组件的方式就是使用的类的继承,ES6 class是目前官方推荐的使用方式,它使用了ES6标准语法来构建,看以下代码:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
render () {
return (
// 注意这里得用this.props.name, 必须用this.props
欢迎进入{this.props.name}的世界
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行结果和之前完全一样,因为JS里没有真正的class,这个class只是一个语法糖, 但二者的运行机制底层运行机制不一样。
函数式组件是直接调用, 在前面的代码里已经有看到
es6 class组件其实就是一个构造器,每次使用组件都相当于在实例化组件,像这样:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
render () {
return (
欢迎进入{this.props.name}的世界
)
}
}
const app = new App({
name: 'react'
}).render()
ReactDOM.render(
app,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3、更老的一种方法
在16以前的版本还支持这样创建组件, 但现在的项目基本上不用
React.createClass({
render () {
return (
)
}
})
1
2
3
4
5
6
7
4、组件的组合、嵌套
将一个组件渲染到某一个节点里的时候,会将这个节点里原有内容覆盖
组件嵌套的方式就是将子组件写入到父组件的模板中去,且react没有Vue中的内容分发机制(slot),所以我们在一个组件的模板中只能看到父子关系
// 从 react 的包当中引入了 React 和 React.js 的组件父类 Component
// 还引入了一个React.js里的一种特殊的组件 Fragment
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'
class Title extends Component {
render () {
return (
欢迎进入React的世界
)
}
}
class Content extends Component {
render () {
return (
React.js是一个构建UI的库
)
}
}
/ 由于每个React组件只能有一个根节点,所以要渲染多个组件的时候,需要在最外层包一个容器,如果使用div, 会生成多余的一层dom
class App extends Component {
render () {
return (
React.js是一个构建UI的库
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(3) 使用prop-types检查props
React其实是为了构建大型应用程序而生, 在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能会造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props设定参数检查,需要安装和使用prop-types:
$ npm i prop-types -S
1
2、状态(state)
状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)
(1) 定义state
第一种方式
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
name: 'React',
isLiked: false
}
render () {
return (
欢迎来到{this.state.name}的世界
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
另一种方式(推荐)
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
name: 'React',
isLiked: false
}
}
render () {
return (
欢迎来到{this.state.name}的世界
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
this.props和this.state是纯js对象,在vue中,data属性是利用Object.defineProperty处理过的,更改data的数据的时候会触发数据的getter和setter,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法setState。
(2) setState
isLiked 存放在实例的 state 对象当中,组件的 render 函数内,会根据组件的 state 的中的isLiked不同显示“取消”或“收藏”内容。下面给 button 加上了点击的事件监听。
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
name: 'React',
isLiked: false
}
}
handleBtnClick = () => {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
欢迎来到{this.state.name}的世界
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
setState有两个参数
第一个参数可以是对象,也可以是方法return一个对象,我们把这个参数叫做updater
参数是对象
this.setState({
isLiked: !this.state.isLiked
})
1
2
3
参数是方法
this.setState((prevState, props) => {
return {
isLiked: !prevState.isLiked
}
})
1
2
3
4
5
注意的是这个方法接收两个参数,第一个是上一次的state, 第二个是props
setState是异步的,所以想要获取到最新的state,没有办法获取,就有了第二个参数,这是一个可选的回调函数
this.setState((prevState, props) => {
return {
isLiked: !prevState.isLiked
}
}, () => {
console.log('回调里的',this.state.isLiked)
})
console.log('setState外部的',this.state.isLiked)
1
2
3
4
5
6
7
8
3、属性vs状态
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
属性能从父组件获取,状态不能
属性可以由父组件修改,状态不能
属性能在内部设置默认值,状态也可以
属性不在组件内部修改,状态要改
属性能设置子组件初始值,状态不可以
属性可以修改子组件的值,状态不可以
state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
如果搞不清 state 和 props 的使用场景,记住一个简单的规则:尽量少地用 state,多用 props。
没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。
4、状态提升
如果有多个组件共享一个数据,把这个数据放到共同的父级组件中来管理
5、受控组件与非受控组件
React组件的数据渲染是否被调用者传递的props完全控制,控制则为受控组件,否则非受控组件。
6、渲染数据
条件渲染
{
condition ? '❤️取消' : '🖤收藏'
}
1
2
3
列表渲染
// 数据
const people = [{
id: 1,
name: 'Leo',
age: 35
}, {
id: 2,
name: 'XiaoMing',
age: 16
}]
// 渲染列表
{
people.map(person => {
return (
-
- {person.name}
-
- age: {person.age}
-
)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
React的高效依赖于所谓的 Virtual-DOM,尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识,React 就可以知道这两个元素只是交换了位置,这个标识就是key,这个 key 必须是每个元素唯一的标识
dangerouslySetInnerHTML
对于富文本创建的内容,后台拿到的数据是这样的:
content = "
React.js是一个构建UI的库
"1
处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候就需要使用dangerouslySetHTML属性,它允许我们动态设置innerHTML
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
content : "
React.js是一个构建UI的库
"}
}
render () {
return (
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
八、事件处理
1、绑定事件
采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React里的事件是驼峰onClick,React的事件并不是原生事件,而是合成事件。
2、事件handler的写法
直接在render里写行内的箭头函数(不推荐)
在组件内使用箭头函数定义一个方法(推荐)
直接在组件内定义一个非箭头函数的方法,然后在render里直接使用onClick={this.handleClick.bind(this)}(不推荐)
直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)
3、Event 对象
和普通浏览器一样,事件handler会被自动传入一个 event 对象,这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是 React中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagation、event.preventDefault 这种常用的方法
4、事件的参数传递
在render里调用方法的地方外面包一层箭头函数
在render里通过this.handleEvent.bind(this, 参数)这样的方式来传递
通过event传递
比较推荐的是做一个子组件, 在父组件中定义方法,通过props传递到子组件中,然后在子组件件通过this.props.method来调用
5、处理用户输入
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
xing: '',
ming: ''
}
}
handleInputChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render () {
const {
xing,
ming
} = this.state
return (
姓:
名:
欢迎您: {xing}{ming}
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
九、表单
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:
名字:
1
2
3
4
5
6
7
此表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。如果你在 React 中执行相同的代码,它依然有效。但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”。
1、受控组件
在 HTML 中,表单元素(如、 和 <select>)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用setState()来更新。</p> <p>我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。</p> <p>例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件:</p> <p>class NameForm extends React.Component {</p> <p> constructor(props) {</p> <pre><code>super(props); this.state = { </code></pre><p> value: ''};</p> <pre><code>this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); </code></pre><p> }</p> <p> handleChange(event) {</p> <pre><code>this.setState({ </code></pre><p> value: event.target.value});<br> }</p> <p> handleSubmit(event) {</p> <pre><code>alert('提交的名字: ' + this.state.value); event.preventDefault(); </code></pre><p> }</p> <p> render() {</p> <pre><code>return ( <form onSubmit={ </code></pre><p> this.handleSubmit}><br> <label><br> 名字:<br> <input type="text" value={ this.state.value} onChange={ this.handleChange} /><br> </label><br> <input type="submit" value="提交" /><br> </form><br> );<br> }<br>}</p> <p>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。</p> <p>对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。</p> <p>2、textarea 标签<br>在 HTML 中, <textarea> 元素通过其子元素定义其文本:</p> <p><textarea><br> 你好, 这是在 text area 里的文本<br>
1
2
3
而在 React 中, 使用 value 属性代替。这样,可以使得使用 <textarea> 的表单和使用单行 input 的表单非常类似:</p> <p>class EssayForm extends React.Component {</p> <p> constructor(props) {</p> <pre><code>super(props); this.state = { value: '请撰写一篇关于你喜欢的 DOM 元素的文章.' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); </code></pre><p> }</p> <p> handleChange(event) {</p> <pre><code>this.setState({ </code></pre><p> value: event.target.value});<br> }</p> <p> handleSubmit(event) {</p> <pre><code>alert('提交的文章: ' + this.state.value); event.preventDefault(); </code></pre><p> }</p> <p> render() {</p> <pre><code>return ( <form onSubmit={ </code></pre><p> this.handleSubmit}><br> <label><br> 文章:<br> <textarea value={ this.state.value} onChange={ this.handleChange} /><br> </label><br> <input type="submit" value="提交" /><br> </form><br> );<br> }<br>}</p> <p>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>请注意,this.state.value 初始化于构造函数中,因此文本区域默认有初值。</p> <p>3、select 标签<br>在 HTML 中,<select> 创建下拉列表标签。例如,如下 HTML 创建了水果相关的下拉列表:</p> <p><select><br> <option value="grapefruit">葡萄柚</option><br> <option value="lime">酸橙</option><br> <option selected value="coconut">椰子</option><br> <option value="mango">芒果</option><br></select><br>1<br>2<br>3<br>4<br>5<br>6<br>请注意,由于 selected 属性的缘故,椰子选项默认被选中。React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。例如:</p> <p>class FlavorForm extends React.Component {</p> <p> constructor(props) {</p> <pre><code>super(props); this.state = { </code></pre><p> value: 'coconut'};</p> <pre><code>this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); </code></pre><p> }</p> <p> handleChange(event) {</p> <pre><code>this.setState({ </code></pre><p> value: event.target.value});<br> }</p> <p> handleSubmit(event) {</p> <pre><code>alert('你喜欢的风味是: ' + this.state.value); event.preventDefault(); </code></pre><p> }</p> <p> render() {</p> <pre><code>return ( <form onSubmit={ </code></pre><p> this.handleSubmit}><br> <label><br> 选择你喜欢的风味:<br> <select value={ this.state.value} onChange={ this.handleChange}><br> <option value="grapefruit">葡萄柚</option><br> <option value="lime">酸橙</option><br> <option value="coconut">椰子</option><br> <option value="mango">芒果</option><br> </select><br> </label><br> <input type="submit" value="提交" /><br> </form><br> );<br> }<br>}</p> <p>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>43<br>44<br>45<br>总的来说,这使得, <input type="text">, <textarea> 和 <select>之类的标签都非常相似—它们都接受一个 value 属性,你可以使用它来实现受控组件。</p> <p>注意</p> <p>你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:</p> <p><select multiple={true} value={['B', 'C']}><br>1<br>class MulFlavorForm extends React.Component {</p> <p> constructor(props) {</p> <pre><code>super(props); this.state = { value: "coconut", arr: [], options: [ { value: "grapefruit", label: "葡萄柚" }, { value: "lime", label: "酸橙" }, { value: "coconut", label: "椰子" }, { value: "mango", label: "芒果" } ] }; this.handleChange = this.handleChange.bind(this); </code></pre><p> }</p> <p> handleChange(e){</p> <pre><code>let idx = this.state.arr.findIndex(item=>{ return item === e.target.value }) if (idx >= 0) { this.state.arr.splice(idx,1); } else { this.state.arr.push(e.target.value); } let arr = this.state.arr; this.setState({ </code></pre><p> arr});<br> }</p> <p> render() {</p> <pre><code>return ( <div> <select multiple={ </code></pre><p> true} value={<br> this.state.arr} onChange={<br> this.handleChange}><br> {<br> this.state.options.map((item,index) => {</p> <pre><code> return <option value={ </code></pre><p> item.value} key={<br> index}>{<br> item.label}</option>;<br> })}<br> </select><br> </div><br> );<br> }<br>}</p> <p>export default Test4;</p> <p>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>43<br>44<br>45<br>46<br>47<br>48<br>49<br>50<br>51<br>52<br>53<br>54<br>55<br>56<br>57<br>58<br>59<br>60<br>61<br>62<br>63<br>64<br>65<br>4、处理多个输入<br>当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。</p> <p>class Reservation extends React.Component {</p> <p> constructor(props) {</p> <pre><code>super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); </code></pre><p> }</p> <p> handleInputChange(event) {</p> <pre><code>const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); </code></pre><p> }</p> <p> render() {</p> <pre><code>return ( <form> <label> 参与: <input name="isGoing" type="checkbox" checked={ </code></pre><p> this.state.isGoing}<br> onChange={<br> this.handleInputChange} /><br> </label><br> <br /><br> <label><br> 来宾人数:<br> <input name="numberOfGuests" type="number" value={ this.state.numberOfGuests} onChange={ this.handleInputChange} /><br> </label</p> <p>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>43<br>44<br>45<br>46<br>47<br>48<br>49<br>50</p>