# 02_面向组件编程
📢 大家好,我是Milo
📢 这篇文章是学习 React 中 组件的学习笔记
# 一、组件的使用
当应用是以多组件的方式实现,这个应用就是一个组件化的应用
注意:
组件名必须是首字母大写
虚拟DOM元素只能有一个根元素
虚拟DOM元素必须有结束标签
< />
渲染类组件标签的基本流程
React 内部会创建组件实例对象
调用
render()
得到虚拟 DOM ,并解析为真实 DOM插入到指定的页面元素内部
# 1. 函数式组件
//1.先创建函数,函数可以有参数,也可以没有,但是必须要有返回值 返回一个虚拟DOM
function Welcome(props) {
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h1>Hello, {props.name}</h1>;
}
//2.渲染组件到页面
ReactDOM.render(<Welcome name = "milo" />,document.getElementById("div"));
2
3
4
5
6
7
上面的代码经历了以下几步
- 我们调用
ReactDOM.render()
函数,并传入<Welcome name="milo" />
作为参数。 - React调用
Welcome
组件,并将{name: 'milo'}
作为 props 传入。 Welcome
组件将Hello, milo
元素作为返回值。- ReactDOM 将 DOM高效地更新为
Hello,milo
。
# 2. 类式组件
# 2.1 什么是类?
复习类的相关知识
类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
如果
A类
继承了B类
,且A类
中写了构造器,那么A类
构造器中的super
是必须要调用的。类中所定义的方法,都放在了类的原型对象上,供实例去使用。
创建一个Person类
//创建一个Person类
class Person {
//构造器方法
constructor(name,age){
//构造器中的this是谁?—— 类的实例对象
this.name = name
this.age = age
}
//一般方法
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我年龄是${this.age}`);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
创建一个Student类,继承于Person类
//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name,age)
this.grade = grade
this.school = '尚硅谷'
}
//重写从父类继承过来的方法
speak(){
console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`);
this.study()
}
study(){
//study方法放在了哪里?——类的原型对象上,供实例使用
//通过Student实例调用study时,study中的this就是Student实例
console.log('我很努力的学习');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
创建一些实例对象,调用方法
const p1 = new Person('milo',18);
console.log(p1); //Person {name: 'milo', age: 18}
p1.speak(); //我叫milo,我年龄是18
const p2 = new Student('milo2',22,'大四');
console.log(p2); //Student {name: 'milo2', age: 22, grade: '大四', school: '工程大'}
p2.speak(); //我叫milo2,我年龄是22,我读的是大四年级
2
3
4
5
6
# 2.2 类式组件
复习结束,开始创建类式组件
// 1.创建类式组件
class MyComponent extends React.Component{
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
2
3
4
5
6
7
8
9
10
11
上面的代码经历了以下几步
- 我们调用
ReactDOM.render()
函数,并传入<MyComponent/>
作为参数。 - React 调用
MyComponent
组件。 MyComponent
组件将我是用类定义的组件(适用于【复杂组件】的定义)
元素作为返回值。- ReactDOM 将 DOM 高效地更新为
我是用类定义的组件(适用于【复杂组件】的定义)
。
# 3. 其他知识
包含表单元素的组件分为非受控租价与受控组件
- 受控组件:表单组件的输入组件随着输入并将内容存储到状态中(随时更新)
- 非受控组件:表单组件的输入组件的内容在有需求的时候才存储到状态中(即用即取)
# 二、组件实例三大属性
# 1. state
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
简单的说就是组件的状态,也就是该组件所存储的数据
# 1.1 state在类式组件Weather中的使用——标准写法
需求: 定义一个展示天气信息的组件
默认展示天气炎热或凉爽
点击文字切换天气
- 首先创建
Weather
类式组件
//1.创建组件
class Weather extends React.Component{
//初始化状态
}
2
3
4
- 在
Weather
类中使用构造器初始化状态
//构造器调用几次? ———— 1次
constructor(props){
super(props)
//初始化状态
this.state = {isHot:false}//天气热么:默认不热
}
2
3
4
5
6
这里state
是组件对象最重要的属性,值是对象(可以包含多个key-value
的组合),形如{isHot:false,wind:'微风'}
3. render
读取state
状态,return
中返回要输出的内容
//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render(){
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
2
3
4
5
6
- 这里
return
中,在<h1>
标签内写onclick
事件时,要写成onClick
! - 组件中
render
方法中的this
为组件实例对象
- 自定义方法
changeWeather
,用来更新组件的state
,以此来更新对应的页面显示(重新渲染组件)
//changeWeather调用几次? ———— 点几次调几次
changeWeather(){
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
}
2
3
4
5
6
7
changeWeather
放在哪里? ————Weather
的原型对象上,供实例使用
注意⚠️!!!
- 由于
changeWeathe
r是作为onClick
的回调,所以不是通过实例调用的,是直接调用 - 并且类中的方法默认开启了局部的严格模式,所以
changeWeather
中的this
为undefined
- 那找不着初始化状态中的
isHot
怎么办?为了解决这个问题,有两种方法:
法一:bind()方法
通过函数对象的bind()
方法,强制绑定this
,需要在构造器中加入下面一行代码
//解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this)
2
法二:赋值语句的形式+箭头函数(常用!)
- 使用赋值语句的形式创建自定义方法,这样
changeWeather
从Weather
类的原型对象方法变成了实例自身的方法 - 箭头函数一大特点是:没有自己的
this
,会找其外层函数的this
作为其this
使用,其外层就是组件实例自身的this
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
//获取原来的isHot值
const isHot = this.state.isHot
//状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
}
2
3
4
5
6
7
- 渲染组件到页面
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
2
总结(标准写法完整代码)
//1.创建组件
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:false}
//解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
render(){
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather(){
//获取原来的isHot值
const isHot = this.state.isHot
//状态通过setState进行更新
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1.2 简写方式(开发常用)
- 自定义方法用赋值语句的形式+箭头函数创建,就不需要在构造器中强制绑定
this
了 - 直接初始化
state
状态,就不用在构造器中初始化了 - 因为以上两点,所以不需要构造器
constructor(){}
了
//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {isHot:false}
render(){
//读取状态
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
//获取原来的isHot值
const isHot = this.state.isHot
//状态通过setState进行更新
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1.3 在优化过程中遇到的问题总结
组件中的
render
方法中的this
为组件实例对象render()
的执行次数是1+n
(1 为初始化时的自动调用,n 为状态更新的次数)
组件自定义方法中由于开启了严格模式,
this
指向undefined
如何解决通过
bind
改变this
指向推荐采用箭头函数,箭头函数的
this
指向(✅开发常用方法)
state
使用注意事项- 使用的时候通过
this.state
调用state
里的值 - 在类式组件中定义
state
- 通过构造器初始化
state
- 推荐直接在类中添加属性
state
来初始化(✅开发常用方法)
- 通过构造器初始化
- 修改
state
不能直接修改,状态要通过setState()
进行更新 - 在执行
setState
操作后,React
会自动调用一次render()
- 使用的时候通过
⚠️注意:在类式组件的函数中,不能直接修改state
值
this.state.weather = '凉爽'
页面的渲染靠的是
render
函数
这时候会发现页面内容不会改变,原因是 React
中 state
不允许直接修改,而是通过类的原型对象上的方法 setState()
setState()
this.setState(partialState, [callback]);
partialState
: 需要更新的状态的部分对象callback
: 更新完状态后的回调函数
有两种写法
写法1:直接写更新的部分对象
this.setState({isHot: true})
写法2:传入一个函数,返回x需要修改成的对象,参数为当前的 state
// 传入一个函数,返回x需要修改成的对象,参数为当前的 state
this.setState(state => ({count: state.count+1});
2
setState
是一种合并操作,不是替换操作
# 2. props
与state
不同,state
是组件自身的状态,而props
则是外部传入的数据
# 2.1 props在类式组件中使用
需求: 自定义用来显示一个人员信息的组件
姓名必须指定,且为字符串类型;
性别为字符串类型,如果性别没有指定,默认为男
年龄为字符串类型,且为数字类型,默认值为18
- 首先创建类式组件Person
class Person extends React.Component{
render(){
//接收数据
const {name,age,sex} = this.props
//显示数据
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 引入prop-types库,用于对组件标签属性进行限制
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
2
对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number//限制age为数值
}
2
3
4
5
如果有函数需要限制,用PropTypes.func
speak:PropTypes.func//限制speak为函数
- 指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
2
3
4
- 渲染组件到页面
ReactDOM.render(<Person name="Milo"/>,document.getElementById('test'))
总结(标准写法完整代码)
class Person extends React.Component{
render(){
//接收数据
const {name,age,sex} = this.props
//显示数据
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number//限制age为数值
}
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
ReactDOM.render(<Person name="Milo"/>,document.getElementById('test'))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 2.2 简写方式(开发常用)
- 用
propTypes
和defaultProps
两个属性来操作props
的规范和默认值时,可以使用static
直接添加在类里面,(类式组件的原型对象上)
//创建组件
class Person extends React.Component{
//对标签属性进行类型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
//指定默认标签属性值
static defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
render(){
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="Milo"/>,document.getElementById('test'))
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
# 2.3 props在类式组件总结
在使用的时候可以通过 this.props
来获取值 类式组件的 props
:
通过在组件标签上传递值,在组件中就可以获取到所传递的值
在构造器里的
props
参数里可以获取到props
constructor(props){ super(props) console.log('constructor',this.props); }
1
2
3
4- 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
- 构造器可以不写
用
propTypes
和defaultProps
两个属性来操作props
的规范和默认值时,可以使用static
直接添加在类里面,(类式组件的原型对象上)可以通过
...
展开运算符来简化
//渲染组件到页面
const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test'))
2
3
# 2.4 props在函数式组件中使用
函数在使用props的时候,是作为参数进行使用的(props)
- 创建函数式组件Person
function Person (props){
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
2
3
4
5
6
7
8
9
10
- 对组件标签属性进行限制和设置默认值
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number//限制age为数值
}
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
2
3
4
5
6
7
8
9
- 在函数式组件中使用
propTypes
和defaultProps
两个属性来操作props
的规范和默认值时,只能放在函数外面定义
- 渲染组件到页面
ReactDOM.render(<Person name="Milo"/>,document.getElementById('test'))
# 2.5 props在函数式式组件总结
函数组件的 props
定义:
- 在组件标签中传递
props
的值 - 组件函数的参数为
props
- 在函数式组件中使用
propTypes
和defaultProps
两个属性来操作props
的规范和默认值时,只能放在函数外面定义(也是原型对象)
# 3. refs
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render
方法中创建的 React 元素。
在我们正常的操作节点时,需要采用DOM API 来查找元素,但是这样违背了 React 的理念,因此有了
refs
有三种操作refs
的方法,分别为:
- 字符串形式
- 回调形式
createRef
形式
需求: 自定义组件,功能说明如下:
点击按钮, 提示第一个输入框中的值
当第2个输入框失去焦点时, 提示这个输入框中的值
第一步:创建类式组件(第一步三种方法都一样)
//创建组件
class Demo extends React.Component{
render(){
return(
<div>
<input type="text" placeholder="点击按钮提示数据"/>
<button>点我提示左侧的数据</button>
<input type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
# 3.1 字符串形式refs
先给DOM元素添加ref属性,这里使用ref="input1"
的方式,让showData等功能使用时,从this.refs
获取input
标签。
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
2
3
编写按钮功能和失去焦点功能,并使用字符串形式this.refs
获取input
标签
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
2
3
4
5
6
7
8
9
10
# 3.2 回调形式refs
(开发常用)
先给DOM元素添加ref属性,组件实例的ref
属性传递一个回调函数c => this.input1 = c
(箭头函数简写),这样会在实例的属性中存储对DOM节点的引用,使用时可直接从this
拿,或者this.input1
来使用
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
2
3
c
会接收到当前节点作为参数,ref
的值为函数的返回值,也就是this.input1 = c
,因此是给实例下的input1
赋值
编写按钮功能和失去焦点功能,并使用字符串形式refs
获取input
标签
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
2
3
4
5
6
7
8
9
10
缺点:
如果 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
解决方法:
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
先给DOM元素添加ref属性,ref={this.saveInput},该方式可以避免内联函数的方式,在更新过程会执行两次的问题,但是大多数情况下它是无关紧要的。
<input ref={this.saveInput} type="text"/>
在类式组件中写一个saveInput的函数,这样在实例的属性中存储对DOM节点的引用,使用时直接从this
拿,或者this.input1
来使用
saveInput = (c)=>{
this.input1 = c;
}
2
3
# 3.3 createRef的refs
(推荐写法)
React 给我们提供了一个相应的API,它会自动的将该 DOM 元素放入实例对象中
我们先给DOM元素添加ref属性
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>
2
3
天禹的理解:React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
通过API,创建React的容器,会将DOM元素赋值给实例对象的名称为容器的属性的current
,所以后面调用要从current里拿
MyRef = React.createRef();
MyRef2 = React.createRef();
2
注意:专人专用,好烦,一个节点创建一个容器
编写按钮功能和失去焦点功能,并使用this.myRef.current获取input标签中的内容
//展示左侧输入框的数据
showData = ()=>{
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value);
}
2
3
4
5
6
7
8
注意:我们不要过度的使用 ref,如果发生时间的元素刚好是需要操作的元素,就可以使用事件对象去替代。过度使用有什么问题我也不清楚,可能有 bug 吧
# 4. 事件处理
- React 使用的是自定义事件,而不是原生的 DOM 事件(为了更好的兼容性)
- React 的事件是通过事件委托方式处理的(委托给组件最外层的元素)(为了更加的高效)
- 可以通过事件的
event.target
获取发生的 DOM 元素对象,可以尽量减少refs
的使用
先不使用ref
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
通过事件的 event.target
获取发生的 DOM 元素对象
//展示右侧输入框的数据
showData2 = (event)=>{
alert(event.target.value);
}
2
3
4
我的理解:
当发生事件的元素正好是要操作的元素,可以通过事件的 event.target
获取发生的 DOM 元素对象!
# 5. 非受控组件与受控组件
**需求: **定义一个包含表单的组件
输入用户名密码后, 点击登录提示输入信息
第一步:创建类式组件Login(第一步都一样)
//创建组件
class Login extends React.Component{
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input type="text" name="username"/>
密码:<input type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
这里不在button
里用onClick
事件的方式,而是在form
里用onSubmit
,触发表单提交时调用回调函数
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
alert(`你输入的用户名是:XXX,你输入的密码是:XXX`)
}
2
3
4
用event.preventDefault() 可以阻止表单提交
# 5.1 非受控组件
先给DOM元素添加ref属性,这里用ref的内敛回调函数形式
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>
</form>
2
3
4
5
这样会在实例的属性中存储对DOM节点的引用,使用时可直接从this
拿
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this
alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
}
2
3
4
5
# 5.2 受控组件
使用onChange事件,只要input发生改变(比如输入内容),就回调onChange的函数saveUsername
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
2
3
4
5
创建保存用户名和密码函数,通过事件的 event.target
获取发生事件的 DOM 元素对象,并使用setSate
保存到状态state
中
//保存用户名到状态中
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
//保存密码到状态中
savePassword = (event)=>{
this.setState({password:event.target.value})
}
2
3
4
5
6
7
8
记得state初始化一下
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
2
3
4
5
这样会在state中存储用户和密码,使用时可直接从this.state
拿,记得拿到的是key-value键值对里的username,所以不要.value!!
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
2
3
4
5
6
# 5.3 总结
非受控组件:现用现取就是非受控,使用了ref!
受控组件:随着你的输入,维护状态就是受控。类似Vue里的双向数据绑定。避免了使用ref!
# 三、高阶函数
关于这部分的知识,之前的笔记有记过了
链接Javascript高阶函数 (opens new window),关于AOP,偏函数,柯里化都有不错的记录
# 1 什么是高阶函数?
如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
# 2 函数的柯里化
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
2
3
4
5
6
7
这里使用高阶函数中的函数柯里化对5.2 受控组件中的需求做优化
对onChange事件的回调函数this.saveUsername增加一个参数
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername('username')} type="text" name="username"/>
密码:<input onChange={this.savePassword('password')} type="password" name="password"/>
<button>登录</button>
</form>
2
3
4
5
注意:onChange事件里必须是一个函数,所以要对saveUsername函数进行柯里化,使得this.saveUsername('username')依然返回一个函数。
//保存表单数据到状态中
saveFormData = (dataType)=>{
return (event)=>{
this.setState({[dataType]:event.target.value})
}
}
2
3
4
5
6
注意:因为对象的属性名会默认转换成字符串类型,es6中的[]可以动态获取对象中的key值
所以这里从回调函数拿到dataType参数后,要用[dataType]的方式来使用
# 3 不用函数柯里化的实现
不用函数柯里化也可以实现,只要保证onChange事件里必须是一个函数即可,那么回调函数就可以改成
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={event => this.saveFormData('username',event) } type="text" name="username"/>
密码:<input onChange={event => this.saveFormData('password',event) } type="password" name="password"/>
<button>登录</button>
</form>
2
3
4
5
onChange事件里箭头函数依然是一个函数
//保存表单数据到状态中
saveFormData = (dataType,event)=>{
this.setState({[dataType]:event.target.value})
}
2
3
4