react 中的高阶函数和柯里化
这一篇博文我们说一下 高阶函数 和 柯里化,这两个次可能第一次听说,不知道是啥意思,我们先不管他哈,记得上一篇博客,我们实现了一个登陆的案例是吧?输入用户名和密码,点击登陆按钮,弹出用户名和密码输入的问题,我们跟随着这个案例来思考一些问题。
上篇博客案例
代码:
<!-- 此处必须写 text/babel -->
<script type="text/babel">
// 创建组件
class Login extends React.Component {
state = { username: '', password: '' }
handleSubmit = (event) => {
event.preventDefault() // 阻止默认事件,即表单提交组织
const { username, password } = this.state
alert(`你输入的账号是 ${username},你输入的密码是 ${password}`)
}
saveUsername = (event) => {
this.setState({ username: event.target.value })
}
savePassword = (event) => {
this.setState({ password: event.target.value })
}
render() {
return (
<form onSubmit={this.handleSubmit}>
账号:<input onChange={this.saveUsername} type="text" name="username" /> <br />
密码:<input onChange={this.savePassword} type="passwofd" name="password" /><br />
<button>登 录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById("app"))
</script>
效果:
高阶函数
我们看上面的代码哈,我们在保存账号密码的时候呢,写了两个含糊,在输入框改变的时候分别调用的这两个函数,但是我们观察这两个函数:
// 保存账号到状态
saveUsername = (event) => {
this.setState({ username: event.target.value })
}
// 保存密码到状态
savePassword = (event) => {
this.setState({ password: event.target.value })
}
有没有觉得这两个方法高度相似?这是只保存密码和账号,如果业务比较大,比如注册,需要保存用户名、密码、确认密码、邮箱、电话、性别、地址.... 这样的话会异常的繁琐,代码会特别的冗余。有没有什么好的办法解决呢?
比如我们就写一个方法,根据传进的参数确定保存的是啥,怎么修改?
那在输入框的change事件里面,就需要修改一下吧
账号:<input onChange={this.saveFormData('username')} type="text" name="username" /> <br />
密码:<input onChange={this.saveFormData('password')} type="passwofd" name="password" /><br />
这样改吧?都用 saveFormData 方法,根据传进的是 username 还是 password 来判断保存的是啥。
那我们得写一个 saveFormData 方法吧?那写一个,这样的话,之前的两个保存方法就可以舍弃了。
// 保存表单数据到状态中
saveFormData =(dataType) =>{
console.log(dataType)
}
dataType 就是我们传进的 username 或者是 password 吧?这样是有问题的,什么问题呢?之前博客点击事件的时候我们说过,onChange={ } 这个花括号里面需要写 js 表达式,如果我们给方法加上 () 了的话,他会被直接执行对吧?
我们直接刷新看一下效果:
我们看到哈,一刷新页面,控制台直接打印了是吧?这个是肯定的原因也说了。然后我在输入框输入数据之后,
onChange 事件就不执行了,这是为啥?其实也好理解,因为 onChange 接受的是 { } 里面 js 表达式返回的值吧?但是这个 saveFormData 函数有返回值吗?没有!所以是 undefined。onChange 赋值了个 undefined,所以不会被触发,就相当于没有用。就是这个原因。
怎么解决呢?思考一下。
既然 onChange 需要的是一个方法,那就给他一个方法。
// 保存表单数据到状态中
saveFormData =(dataType) =>{
return () => {
}
}
这样就可以啦吧!那你说,onChange 最后回调的是 saveFormData ,还是 saveFormData 里面返回的箭头函数?
很显然是saveFormData 里面返回的箭头函数啊!
所以说 返回的箭头函数可以获取到 event 吧?不确定就打印一下,同时我们把 dataType 也打印一下。
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
console.log(dataType, event.target.value)
}
}
保存看效果:
哎哟,这不就都拿到了嘛,保存的是啥,值是啥,都拿到了。接下来就是简单的保存进状态了。
怎么保存进状态,很多宝子很聪明,是这样写的。
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
this.setState({ dataType: event.target.value })
}
}
额~ 这样肯定是不可以的哈!
你看截图,如果这样写了的话,保存起来之后,既没有改变 username,也没有改变 password,而是创建了新的 dataType 保存了。那正确的保存方式怎么写呢?
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value })
}
}
这样子就可以啦哈
为什么这样写,我说一下吧。首先我们回顾一下 对象的相关知识。
对象的相关知识
假设我有一个变量 a 的值是 name,有一个空对象 obj,我想把 obj 变成 { name: "我是ed." } 怎么做?
<script>
let a = "name"
let obj = {} // {name: '我是ed.'}
obj.name = '我是ed.'
console.log(obj)
</script>
上面代码是没有问题的吧?
当然没问题,但是 a 没有用到啊,如果要用 a 呢?
obj[a] = '我是ed.'
console.log(obj)
这样子就可以了吧?所以说
this.setState({ [dataType]: event.target.value })
这个地方的 dataType 加上中括号就可以了。
好的,这样保存状态尽管在两个地方需要,但是只需要一个函数就可以实现了!
<!-- 此处必须写 text/babel -->
<script type="text/babel">
// 创建组件
class Login extends React.Component {
state = { username: '', password: '' }
handleSubmit = (event) => {
event.preventDefault() // 阻止默认事件,即表单提交组织
const { username, password } = this.state
alert(`你输入的账号是 ${username},你输入的密码是 ${password}`)
}
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value })
}
}
render() {
return (
<form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
账号:<input onChange={this.saveFormData('username')} type="text" name="username" /> <br />
密码:<input onChange={this.saveFormData('password')} type="passwofd" name="password" /><br />
<button>登 录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById("app"))
</script>
效果很完美
案例我们改造完了,那我们改造的
saveFormData 函数就是高阶函数
// 保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value })
}
}
我们看一下高阶函数的定义:
高阶函数:如果一个函数不符合下面两个规范中的任意一个,那该海曙就是高阶函数。
- 若A函数,接受的参数是一个函数,那个A就可以称之为高阶函数。
- 若A函数,调用的返回值依然是一个函数,那A就可以称之为高阶函数。
常见的高阶函数:Promise、setTimeout、arr.map() 等等。
saveFormData 是根据第二条来定义的,同时这个函数使用了 函数柯里化。
什么是函数的柯里化呢?
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式。
这个函数柯里化不好定义哈,我们看一个例子,一个典型的函数柯里化的例子,数字求和:
<script>
function sum(a) {
return (b) => {
return (c) => {
return a + b + c
}
}
}
const result = sum(1)(2)(3)
console.log(result)
</script>
执行结果出来了,很正确!
如果单纯看这个例子的话,觉得这个求和的例子简直是有病!
但是我们这个登陆的案例,不就是一个很合理的柯里化应用场景吗?我想直接穿一个参数告诉存储方法我要存的是啥,我也想告诉这个存储方法我存储的值是多少,但是 存储的是什么我可以告诉他,但是值是多少也就是 event 是多少,我告诉不了,因为 event 是 react 是回调的时候自动生成出来的,我们不能干涉,所以就是用函数的柯里化解决问题。
OK,今天这部分的内容就到这里了。辛苦了。
【本部分相关代码资料】:我是𝒆𝒅. 的 gitee
不使用柯里化
哎哟喂,我较真,我强迫症,我就不用柯里化,可以实现吗?
嘿嘿,也是可以的其实。如果不用,我 saveFormData 方法就接受一个 保存类型 和 值。
// 保存表单数据到状态中
saveFormData = (dataType, value) => {
this.setState({ [dataType]: value })
}
你想啊,这样的话 onChange 事件还要必须要一个函数,那就给他一个呗
账号:<input onChange={(event) => {this.saveFormData('username', event.target.value)}} type="text" name="username" /> <br />
密码:<input onChange={(event) => {this.saveFormData('password', event.target.value)}} type="password" name="password" /><br />
这不就完了嘛!
当然还可以化简
账号:<input onChange={event => this.saveFormData('username', event.target.value)} type="text" name="username" /> <br />
密码:<input onChange={event => this.saveFormData('password', event.target.value)} type="password" name="password" /><br />
好了,今晚的内容就是这些,时间不早了,晚上两点了快,拜拜,晚安啦!