# react 中的高阶函数和柯里化

105 阅读6分钟

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 />

在这里插入图片描述

好了,今晚的内容就是这些,时间不早了,晚上两点了快,拜拜,晚安啦!

在这里插入图片描述