首先来看看需求:

可以看到,这里条件很多,并且最后都是要生成节点,那么就先有请被优化的嘉宾上场吧:
const Create = (row) => {
const currentTarget = getCurrentTarget(id)
const isCurrentTargetOnline = isTargetOnline(currentTarget.status)
const isFile = row.is_file
const isLargeFile = row.length > 1024 * 1024 * 1024
if (isCurrentTargetOnline) {
// 外部已经获取了
if (isWifi) {
if (isFile) {
if (isLargeFile) {
return <Button onClick={() => handleCacheNow(row)}>cache now</Button>
} else {
return (
<Popconfirm
placement="top"
title="在线 && wifi && 大文件"
onConfirm={() => handleCacheNowConfirm(row)}
onCancel={() => handleCacheNowCancel(row)}
okText="No"
cancelText="Yes"
>
<Button>cache now</Button>
</Popconfirm>
)
}
} else {
return (
<Space size="small">
<Button onClick={() => handleUpdate(row)}>cache now</Button>
<Popconfirm
placement="top"
title="在线 && wifi && 大文件"
onConfirm={() => handleCacheNowConfirm(row)}
onCancel={() => handleCacheNowCancel(row)}
okText="No"
cancelText="Yes"
>
<Button>cache now</Button>
</Popconfirm>
</Space>
)
}
}
}
}
原谅我实在没法把这个代码写完......那是在太磨人了
上面的代码暴露了哪些问题呢?
- 首先嵌套的
if-else绝对是罪大恶极,条件语句这种东西一旦多层嵌套起来,自己都不知道哪个条件是那个条件 - 然后这里每个条件最后都是返回组件,那么完全可以将这部分代码抽离出来做一个生成函数,根据参数生成不同的节点
- 开头的状态获取,有的从内部获取,有的从外部获取,也很容易混乱
- handle 函数,明明都是一样的功能,却因为情况不同分了好几个函数来写
那么下面就从这几个角度来优化代码,首先是条件语句,嵌套的条件语句,完全可以改成下面这个样子:(当然最是照着流程图来写,避免遗漏)
const Create = (row) => {
if (isOnline && isWifi && isFile && isLargeFile) {
/* do someting */
} else if (isOnline && isWifi && isFile && !isLargeFile) {
/* do someting */
} else if (isOnline && isWifi && !isFile) {
/* do someting */
} else if (isOnline && !isWifi && isFile && isLargeFile) {
/* do someting */
} else if (isOnline && !isWifi && isFile && !isLargeFile) {
/* do someting */
} else if (isOnline && !isWifi && !isFile) {
/* do someting */
} else if (!isOnline && isFile && isLargeFile) {
/* do someting */
} else if (!isOnline && isFile && !isLargeFile) {
/* do someting */
} else if (!isOnline && !isFile) {
/* do someting */
}
}
这样就从嵌套变成了扁平化的代码,加上注释就能很快的找到对应逻辑链了。当然,这里也可以建一张表:
const Create = (row) => {
const mapConditionToNode = new Map([
[isOnline && isWifi && isFile && isLargeFile, () => {}],
[isOnline && isWifi && isFile && !isLargeFile, () => {}],
[isOnline && isWifi && !isFile, () => {}],
[isOnline && !isWifi && isFile && isLargeFile, () => {}],
[isOnline && !isWifi && isFile && !isLargeFile, () => {}],
[isOnline && !isWifi && !isFile, () => {}],
[!isOnline && isFile && isLargeFile, () => {}],
[!isOnline && isFile && !isLargeFile, () => {}],
[!isOnline && !isFile, () => {}],
])
mapConditionToNode.forEach((value, key) => {
key && value()
})
}
现在代码已经从嵌套地狱里逃出来了,接下来就是节点的生成器。
从逻辑图可以看到,这里目标节点只有两种类型:
- Button
- Popconfirm
其中 Popconfirm 内部又需要 Button,那么只要建两个工厂就好:
const CreateBtn = ({ text, icon, disabled, fn }) => {
return (
<Button icon={<IconDisplay type={icon} />} onClick={fn} disabled={disabled}>
{text}
</Button>
)
}
const CreatePop = (btnConfig, { text: popText, confirmFn, cancelFn }) => {
return (
<Popconfirm
placement="top"
title={popText}
onConfirm={cancelFn}
onCancel={confirmFn}
okText="No"
cancelText="Yes"
>
{CreateBtn(btnConfig)}
</Popconfirm>
)
}
接下来将状态获取统一一下:
const { isOnline, isFile, isLargeFile, isWifi } = getStatus(row)
函数我们根据不同的参数做不同的处理即可,这里就不再赘述。
结语
更加阅读体验:记一次代码优化过程