我的React代码优化之路...(持续更新)

81 阅读3分钟

作为一个刚入门的萌新,入职在这个公司大半年之后,每当看到以前的代码,总会默默骂一句狗屎,忍无可忍开始优化之路

一、 if else if 过长

简单的状态判断

可以通过对象或者ts中的枚举

  function fn(state: number) {
    if (state === 1) {
      console.log('xxxx1')
    } else if (state === 2) {
      console.log('xxxx2')
    } else if (state === 2) {
      console.log('xxxx3')
    }
  }
  
  //使用对象的方式
  function fn1(state: number) {
    const stateObj = {
      1: 'xxxx1',
      2: 'xxxx2',
      3: 'xxxx3'
    }
    console.log(stateObj[state])// key值为数字,不能使用.进行赋值,使用[]
  }

复杂一点的信息提交校验时

经常会见到这样的代码

    async function onSubmit(values: Partial<SubData>) {
        const { tel, isPhone, verCode, password, conimPawd, verCodeKey, ...vals } = values
        if (!tel) {
            message.warning('手机号为空')
        } else if (!isPhone.test(tel)) {
            message.warning('手机号格式不正确')
        } else if (!verCode) {
            message.warning('验证码为空')
        } else if (password === '' || conimPawd === '') {
            message.warning('密码为空')
        } else if (password !== conimPawd) {
            message.warning('密码必须相同')
        } else if (!verCodeKey) {
            message.warning('请获取验证码')
        } else {
            const res: Resolve = await forgetPass(values)
            if (res?.code === 200) {
                message.success('修改成功,请重新登录')
                navigate('/login')
            }
        }
    }

通过循环和return关键字

将连续校验的结构拆解为自上而下的结构,代码逻辑更加清晰

    async function onSubmit(values: Partial<SubData>) {
        const { tel, isPhone, verCode, password, conimPawd, verCodeKey, ...vals } = values
        const rules = [
            { required: !tel, message: '手机号为空' },
            { required: !isPhone.test(tel), message: '手机号格式不正确' },
            { required: !verCode, message: '验证码为空' },
            { required: !password || !conimPawd, message: '密码为空' },
            { required: password !== conimPawd, message: '密码必须相同' },
            { required: !verCodeKey, message: '请获取验证码' }
        ]
        for (let i = 0; i < rules.length; i++) {
            if (rules[i].required) {
                message.error(rules[i].message)
                return
            }
        }
        const res: Resolve = await resetPass(values)
        if (res?.code === 200) {
            message.success('修改成功,请重新登录')
            navigate('/login')
        }
    }

二、自定义hook,摆脱重复CV

这是一个后台页面的大概逻辑

我们可以通过自定义hook来简化这个过程,避免每个页面重复Ctrl+CV

    const [oSrch, setSrch] = useState<SrchData>({ page: 1, pageSize: 10 })
    const [tableData, setTableData] = useState<Res<TableData>>()
    useEffect(() => { getData(), [oSrch])
    async function getData() {
        try {
            const res: Res<T> = await networkReq(oSrch)
            if (res?.code === 0) setData(res?.data)
        } catch (err) {
            console.error(err)
        }
    }
    //查询
    function onSearch(values: Partial<TableData>) {
        const { name, ...vals } = values
        // 对参数的处理
        setSrch({ ...vals, page: 1, pageSize: oSrch.pageSize })
    }
    //分页变化
    function pageChange(page, pageSize) {
        setSrch({ ...oSrch, page, pageSize })
    }

自定义hook代码

可以在这里集中捕获错误,判断获取状态等

/*
networkReq:封装好的网络请求
addParame:需要添加的额外请求参数

返回值
parame:网络请求参数
setParame:改变参数配合useEffect重新请求 (例如:搜索查询)
data:成功后的数据
isSendReq, setSendReq:是否重新请求的开关
loading:请求过程的状态
*/
import { useEffect, useState } from 'react'
const useGetData = <T,>(networkReq: any, addParame?: SrchData) => {
    const [data, setData] = useState<ResData<T>>()
    const [parame, setParame] = useState<SrchData>({ page: 1, pageSize: 10 })
    const [isSendReq, setSendReq] = useState<boolean>(false)
    const [loading, setLoading] = useState<boolean>(false)
    useEffect(() => {
        getData()
    }, [parame, isSendReq])
    async function getData() {
        try {
            setLoading(true)
            const res: Res<T> = await networkReq({ ...parame, ...addParame })
            if (res?.code === 0) {
                setData(res?.data)
                setLoading(false)
            } else {
                //常见错误:例如约定的成功返回值code不为0
                console.warn(res)
                setLoading(false)
            }
        } catch (err) {
            console.error(err)
            setLoading(false)
        }
    }
    return [parame, setParame, data, isSendReq, setSendReq, loading] as [SrchData, any, ResData<T>, boolean, any, boolean]
}
export default useGetData

使用

原先每个页面十几行代码,现在一行结束,告别复制粘贴

    const [oSrch, setSrch, tableData, isSendReq, setSendReq, loading] = useGetData<TableData>(getAdminList)
    //查询
    function onSearch(values: Partial<TableData>) {
        const { name, ...vals } = values
        // 对参数的处理
        setSrch({ ...vals, page: 1, pageSize: oSrch.pageSize })
    }

三、循环方式的选择

当时碰到一个开发需求,后台返回的全国省市区需要二次处理,当时还是初入职场的菜鸡,傻乎乎的用三层for循环

for循环

    const options = [];
    for (let i = 0; i < regionData?.length; i++) {
        options.push({
            value: regionData[i]?.id,
            label: regionData[i]?.name,
            children: [],
        })
        for (let c = 0; c < regionData[i]?.city?.length; c++) {
            options[i].children.push({
                value: regionData[i]?.city[c].id,
                label: regionData[i]?.city[c].name,
                children: [],
            })
            for (let a = 0; a < regionData[i]?.city[c]?.area.length; a++) {
                options[i].children[c]?.children?.push({
                    value: regionData[i]?.city[c]?.area[a].id,
                    label: regionData[i]?.city[c]?.area[a].name
                })
            }
        }
    }

使用map

可以看到可读性更好,总体也更加简洁(这里先不考虑效率问题)

    const options = regionData?.map(province => ({
        value: province.id,
        label: province.name,
        children: province.city.map(city => ({
            value: city.id,
            label: city.name,
            children: city.area.map(area => ({
                value: area.id,
                label: area.name
            }))
        }))
    }))