请教大佬,把一段代码用函数式编程变得更加优雅

2022-04-14 22:30:26 +08:00
 chenliangngng

最近在学函数式编程,总总不能体会它的好处,而且看了几个礼拜,还没有产出一行可以上生产的代码,所以想用最日常的过程式的代码,请教下各位大佬,看看如何优化

// import {message} from 'antd'
// import {fetchApi} from '@/api'
// import moment from 'moment'

// const [detail, setDetail] = React.useState()

const validator = async () => {
	// do something
}

const getDetail = async (params) => {
	await validator();
	try{
    	const res = await fetchApi({name: 'name', ...params, id: 1})
        if (res.code >= 200 && res.code < 400) {
        	const {list, obj} = res.data
            const {date1, num1 = 1, num2, ...objRest} = obj 
        	const formData = {
            	list: list?.map(item => {
                	const {p1, ...itemRest} = item
                    return {
                    	...itemRest,
                        p2: p1
                    }
                }),
                obj: {
                	 ...objRest,
                     date1: date1 && moment(date1).format("YYYY-MM-DD"),
                     num1: Number(num1),
                     num2: Number(num2),
                }
            }
            setDetail(formData)
        } else {
        	throw Error(res.msg)
        }
    } catch (err) {
    	message.error(err?.message || '请求错误')
    }
}

这个应该是前端最基础的接口处理了,基本上囊括了我会碰到的各种问题。我感觉能用 ramdajs 或者 lodash/fp 等函数库把这个函数用函数式编程优化了,基本上就 fp 通关了

1601 次点击
所在节点    前端开发
4 条回复
vance123
2022-04-14 22:35:49 +08:00
问题应该不在过程式或是函数式。好的代码一次只做一件事,你这段代码一次做了 n 件事
chenliangngng
2022-04-14 22:45:52 +08:00
@vance123 中间这个字段映射的部分呢
```javascript
const {list, obj} = res.data;
const {date1, num1 = 1, num2, ...objRest} = obj
const formData = {
list: list?.map(item => {
const {p1, ...itemRest} = item
return {
...itemRest,
p2: p1
}
}),
obj: {
...objRest,
date1: date1 && moment(date1).format("YYYY-MM-DD"),
num1: Number(num1),
num2: Number(num2),
}
}
```
GeruzoniAnsasu
2022-04-14 23:00:42 +08:00
函数式的精髓在于「符号演算」,本质上是一些公式代换,所以是在处理复杂对象关系和约束时才比较好用,并不是说函数式就比过程式更优雅,先走出误区。

再说实例。
你展示的一个 getdetail 的过程:
1. 调接口
2. 验证拉取结果
3. 对参数进行「变换」
这个描述本身就非常过程化,有明确的分段步骤,并不存在关系约束的语义,所以用函数式写本来也不适合。

当然也不是不可以,我们换个描述:

1. setdetail 接受「 raw 数据的变换结果」,最终将结果闭包展开,暂时先不管它内部什么样
2. 「 raw 变换结果」可以写为将「变换」应用到「 raw 」上 ← 函数化
3. 「 raw 数据」可以看做将「请求提取子(包含验证子)」应用在「数据源」上←函数化
4. 「数据源」是一个「输入请求类型然后重封装为响应类型的 monad 」←函数化
5. 「请求类型」 由 「请求类型变换子」应用在「请求数据」上得到←函数化


你觉得这优雅吗,我觉得一点也不,还非常麻烦和抽象,每一个「变换子」(即函数)都得绞尽脑汁才写出来,这些步骤也不可能比过程描述要少。


像 map 和 filter 这样的函数已经是函数式最适用的最小场合了,提供一个输入数组和输出数组的约束函数,把这个约束应用在原数组上得到新数组。这不函数化吗,不够优雅吗?
walpurgis
2022-04-15 10:08:00 +08:00
去状态,首当其冲是减少 useState ,用 react query 或 swr 管理数据获取,getDetail 改成 return formData ,getDetail 就变成了一个伪纯函数,描述了 params 和 detail 的映射关系
这么做从根本上避免了在快速改变 params 时,多个协程都去 setDetail 产生的竞态问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/847038

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX