> 文档中心 > 【React函数的柯里化】4、什么?这玩意儿不仅能装x,还能优化代码?

【React函数的柯里化】4、什么?这玩意儿不仅能装x,还能优化代码?


系列文章目录

👏欢迎来到我的React系列文章
🍭本系列文章从React入门开始,涵盖了React的一切基础,属于是从零开始的一个系列
🍭文章会以图文结合-动图-代码实现-解释代码的形式带领大家走进React的世界
🍭本系列会从React v16.8 开始 => 直到React v 18.0 新版旧版一起抓
🍭持续更新中~希望大家能够喜欢,系列文章👉点这里
🌈博客主页👉点这里
👋回见~

  1. React入门与概览(JSX语法)
  2. 面向组件编程——组件实例的三大核心属性state、props和refs超详解
  3. 受控组件与非受控组件(vue和react的双向绑定分别是怎么实现的?)
  4. React函数的柯里化(什么?这玩意儿不仅能装x,还能优化代码?)

高阶函数与函数柯里化

没错,我要说的就是高阶函数以及一个关于函数的高阶技术——函数柯里化,掌握好了这项技术,走出去跟人装x岂不是手拿把掐,手到擒来~当然,这项技术还能优化你的代码,让你的代码看上去更加的简洁哟~

【React函数的柯里化】4、什么?这玩意儿不仅能装x,还能优化代码?
我要开始装x了~

高阶函数与函数柯里化的定义

高阶函数:

​ 如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数

​ 1、若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。

​ 2、若A函数,调用的返回值仍是一个函数,那么A就可以称之为高阶函数。

​ 常见的高阶函数:Promise(参数是函数)、setTimeout(参数是函数)、arr.map(参数是函数)等等…

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式。

定义属实是晦涩难懂,接下来就用一个小demo讲解一二吧~

高阶函数与函数柯里化的示例

示例效果

在这里插入图片描述

需求:
1、定义一个包含表单的组件
2、输入用户名密码后,点击登录提示输入信息

其实本文中所用的示例与上一篇系列文章所用示例完全一样,我将用这个示例来为你讲解高阶函数与函数的柯里化,从而对之前的代码优化一番。

一般写法

在这里插入图片描述

我们可以看出,如果form表单的字段比较多的话,每次需要把数据存储进state中(受控组件),都需要使用一个函数saveValue xx,还得手动把一个个的函数saveValue xx挂载到组件实例对象上,拜托~这样真的容易让接手你的代码的同事问候你的家人(手动doge~)

我们可以用高阶函数与函数的柯里化写法对这一段屎山代码进行优化,看好了小子,这一刀会很帅!

优化代码实现

<script type="text/babel">    class Login extends React.Component {      state = { username: '', password: ''      }      render() { return (   <form action="" onSubmit={this.handleSubmit} >     用户名:<input onChange={this.saveFormData('username')} value={this.state.username}  type="text" name="username" /> &nbsp;     密码:<input onChange={this.saveFormData('password')}      type="password" name="password" />&nbsp;&nbsp;     <button>登录</button>   </form > )      }      handleSubmit = (event) => { event.preventDefault()  // 阻止默认事件(阻止表单提交) const { username, password } = this.state alert(`你输入的用户名是${username},你输入的密码是${password}`)      }      // 这个函数的返回值交给onChange作为回调      // 这个saveFormData就是一个高阶函数      // 同时也是函数的柯里化写法,两个函数接收到的两个参数,dataType和event最后统一处理      saveFormData = (dataType) => { // 把返回值变成一个函数,由于返回值是一个箭头函数,箭头函数本身没有this指向, // this指向默认是包裹箭头函数外层的函数的this指向,也就是saveFormData这个函数的this指向。 // 如果返回值使用的是普通函数,那么还需要考虑更改this指向的问题 return (event) => {   console.log(dataType, event.target.value)   // this.setState({ [event.target.name]: event.target.value }); 这种写法也可以   this.setState({ [dataType]: event.target.value }); }      }    }    ReactDOM.render(<Login />, document.getElementById('test'))  </script>

在优化后的代码中我们可以看到,不论表单有多少个字段需要存储,我们都只需要维护一个函数到组件的实例对象上,也就是saveFormData().当然,这里以两个input作为示范,分别是username和password。

疑问1:表单事件绑定

本代码块的form表单中,绑定的是onchange事件,那么该事件调用的是?

<form action="" onSubmit={this.handleSubmit} >     用户名:<input onChange={this.saveFormData('username')} value={this.state.username}  type="text" name="username" /> &nbsp;     密码:<input onChange={this.saveFormData('password')}      type="password" name="password" />&nbsp;&nbsp;     <button>登录</button> </form >

解释

1、onChange={this.saveFormData}表示,将该Login类实例对象下的saveFormData函数交给onChange事件作为回调函数使用,每当input框中的值被改变时,调用该函数。

2、这里的onChange={this.saveFormData('username')} onChange事件实际上绑定的是this.saveFormData函数的返回值。

疑问2:哪个函数是高阶函数?

本段代码中的saveFormData()就是一个高阶函数,该函数的返回值本身是一个回调函数。
————也就是说,onChange事件绑定的就是该函数的返回值本身的这个回调函数。

saveFormData = (dataType) => { // 把返回值变成一个函数,由于返回值是一个箭头函数,箭头函数本身没有this指向, // this指向默认是包裹箭头函数外层的函数的this指向,也就是saveFormData这个函数的this指向。 // 如果返回值使用的是普通函数,那么还需要考虑更改this指向的问题 return (event) => {   console.log(dataType, event.target.value)   // this.setState({ [event.target.name]: event.target.value }); 这种写法也可以   this.setState({ [dataType]: event.target.value }); }    }

我们再来看看高阶函数的定义吧:
如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数

​ 1、若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。

​ 2、若A函数,调用的返回值仍是一个函数,那么A就可以称之为高阶函数。

函数saveFormData()的返回值仍然是一个函数,并且是交给onChange事件作为回调的函数,那么显然,函数saveFormData()就是一个高阶函数啦~~

疑问3:哪个函数使用了函数柯里化?

演示函数柯里化

function sum(a, b, c) {     return a + b + c}const result = sum(1,2,3)// 这是一个普通的求和函数,调用该函数可以求出传入的三个参数之和

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式。

// 函数的柯里化写法function sum(a) {  return (b) => {    return (c) => {      return a + b + c    }  }}const result = sum(1)(2)(3)// 这是使用了函数柯里化写法的求和函数,通过函数sum()的调用,不断的返回函数,// 每次返回的函数中调用一次参数,最后统一对传入的三个参数a, b, c进行处理,符合函数柯里化的定义

那么本文示例中哪个函数使用了柯里化的写法呢?

saveFormData = (dataType) => { // 把返回值变成一个函数,由于返回值是一个箭头函数,箭头函数本身没有this指向, // this指向默认是包裹箭头函数外层的函数的this指向,也就是saveFormData这个函数的this指向。 // 如果返回值使用的是普通函数,那么还需要考虑更改this指向的问题 return (event) => {   console.log(dataType, event.target.value)   // this.setState({ [event.target.name]: event.target.value }); 这种写法也可以   this.setState({ [dataType]: event.target.value }); }    }

当然还是这个saveFormData()函数啦,函数调用时传入参数dataType,返回值中仍是一个回调函数,再次传入参数event,最后,在this.setState中统一处理这两个参数,完美符合函数的柯里化的定义,所以,这个saveFormData()函数不仅是一个高阶函数,同时又使用了函数的柯里化写法

疑问4:同时传入两个参数不行吗?

细心的人想必发现了,saveFormData()为嘛要先传dataType再传event啊,直接两个一块儿传不得了?

如果参数真是一块儿传进来,会发生什么情景呢?

saveFormData = (dataType, event) => { this.setState({ [dataType]: event.target.value });}

这样的话,saveFormData()函数压根接收不到参数event。

因为这个函数的调用取决于我们自己传入的参数,也就是‘username’,而event是React调用函数时才会生成的。
onChange = {this.saveFormData('username')}这种写法含义是:
——将username传入函数saveFormData同时调用此函数,调用后的返回值交给onChange事件作为回调函数使用,也就是React监听到事件onChange发生时,才会调用返回值中的回调函数,从而才能接收到event参数。也就是前面说的,自己传入参数‘username’根本接收不到参数event。只有返回值中的函数才能接收到event参数。

所以说,saveForm真的就不能直接接收到参数event了吗?
当然——————不是啦

我们也可以不用柯里化去实现这个示例。

疑问5:不用柯里化能实现吗?

render() {  return (    <form action="" onSubmit={this.handleSubmit} >      用户名:<input onChange={event => { this.saveFormData('username', event) }} value={this.state.username} type="text" name="username" /> &nbsp;      密码:<input onChange={event => this.saveFormData('password', event)} type="password" name="password" />      &nbsp;&nbsp;      <button>登录</button>    </form >  )}saveFormData = (dataType, event) => {  this.setState({ [dataType]: event.target.value });}

解释

既然React去调用onChange事件中绑定的函数才能接收到参数event,那么我们就直接给onChange一个回调函数就好啦。让React去帮我们调用onChange事件,接收到event参数后,将dataTypeevent都传入函数saveFormData(),这样,就不需要使用函数的柯里化,只要直接将数据保存到state中即可哟~

当然了,这样的写法显然比函数的柯里化写法更为复杂,每个字段的onChange身上还得写一长串的内联箭头函数

✅✅

还是柯里化香啊,又能装x,还能优化代码~(首尾呼应!首尾呼应啊喂!)

在这里插入图片描述
回见~

开发者涨薪指南 【React函数的柯里化】4、什么?这玩意儿不仅能装x,还能优化代码? 48位大咖的思考法则、工作方式、逻辑体系