React Hooks避坑--持续更新

435 阅读4分钟

前言

VueReact Class 再到 React Hooks,使用过程中其实遇到过很多坑,在面对问题的时候,感觉每一个都非要的“要命”,然而解决完之后又感觉不是很大的问题,但是这些小问题如果进行深究的话,是可以学习到很多关于框架的原理知识的。

在接下来的工作以及学习中,我会一直持续更新各种“坑”,希望大家可以共同学习以及进步,奥力给~

坑One

一、场景描述

使用React HooksAntd Table组件实现列表数据展示

  1. 通过用户角色控制“操作”列是否展示(超级管理员无操作权限,只能查看)
  2. 点击“去支付”跳转支付页面,若无正常进行支付流程,在返回列表要刷新列表,并展示倒计时

二、问题描述

  1. 点击“查看详情”按钮,跳转详情页,传递的角色为初始化角色,而不是根据接口查询返回的最新数据;
  2. 点击“去支付”跳转到支付页面,返回到上一级页面时,接口重新调用且状态已修改为“待付款”,但是并没有在页面展示最新状态及倒计时;
  3. 倒计时实现过程出现页面数据不重新渲染的问题;

三、实现细节(存在问题)

// 用户角色:0-超级管理员、1-代理商
const [role, setRole] = useState<number>(0);

// 从接口请求的数据源
const [list, setList] = useState<any []>([]);

// list数据结构如下:
{
    key: '1',
    id: 'NO202203062235',
    status: 1,
    money: '0.01',
}

// 定义表格
const [columns, setColumns] = useState<any []>([
    {
        title: '合同ID',
        dataIndex: 'id',
        key: 'id',
    },
    {
        title: '支付状态',
        dataIndex: 'status',
        key: 'status',
        render(text: any) {
            const statusObj: any = {
                1: '未支付',
                2: '待付款',
                3: '已支付'
            }
            return statusObj[text] || '-'
        }
    },
    {
        title: '金额',
        dataIndex: 'money',
        key: 'money',
    },
    {
        title: '操作',
        dataIndex: 'operation',
        key: 'operation',
        render(record: any) {
            return (
                <>
                    {
                        record.status === 1 &&
                        <Button onClick={() => payHandler(record)}>去支付</Button>
                    }
                    {
                        record.status === 2 &&
                        <Button onClick={() => cancelPayHandler(record)}>取消支付</Button>
                    }
                    {
                        record.status === 2 && record.count > 0 &&
                        <CountDown count={record.count} />
                    }
                    <Button onClick={() => detailHandler(record)}>查看详情</Button>
                </>
            )
        }
    }
]);

// Table渲染
<Table
    dataSource={list}
    columns={columns}
/>

// JS逻辑
// 1、获取用户角色
const getRole = async () => {
    const res = await api.role();
    res.code === 0 ? setRole(res.role) : '';
    // 根据角色显示“操作”列
    res.role === 0 ? setColumns(columns.filter((item) => item.dataIndex !== 'operation')) : ''
}

// 2、根据角色获取列表数据
const getRole = async () => {
    const res = await api.list();
    res.code === 0 ? setList(res.data) : ''
}

// 3、去支付
const payHandler = () => {
    // 跳转支付页面,url为处理过的页面URL
    window.location.href = url;
}

// 4、查看详情
const detailHandler = () => {
    history.push(`/detail?role=$role}`)
}

useEffect(() => {
    getRole();
    getRole();
}, [])

四、问题原因

1、为什么点击“查看详情”按钮,获取到的role为最初设置的默认值0,而不是根据接口获取的最新值?

原因:

useState初始化值,只有在第一次有效

例如:

// 父组件 App.js
const [name, setName] = useState('张三')
const userInfo = { name }

return <div>
    <button onClick={() => setName('李四')}>setName</button>
    <Child userInfo={userInfo} />
</div>

// 子组件 Child.js
// 【注意】
// render(首次渲染): 初始化 state
// re-render(更新渲染): 只恢复初始化的值(即张三),不会在重新设置新的值,只能使用setName去手动修改
const [name, setName] = useState(userInfo.name)

return <div>
    // 点击App中的 setName 按钮,此处会对应修改
    <p>Child, props name: {userInfo.name}<p>
    // 点击App中的 setName 按钮,此处不会对应修改
    <p>Child, state name: {name}<p>
</div>

实际应用中问题原因:

使用useState [columns, setColumns]来定义表格源,所以表格的操作的“查看详情”中render中已经默认记住role的初始值0,当调用接口异步获取新的角色值之后,由于只调用了setRole修改role的值,并没有手动调用setColumns去修改columns,所以columns中的数据依旧是 默认的初始值,因此role一直是默认值0

解决方法:

  1. columns不使用useState初始化,只定义为一个常量值,这样操作一列无法去除,可以使用‘-’展示操作列;
  2. columns不使用useState初始化,只定义为一个常量值,操作列可以根据条件判断进而控制列宽即width0
  3. 依旧使用useState设置columns,当获取到角色时,除了设置role外,还要手动设置columns; 4.既然无法获取最新的role,那么在获取list列表的时候,可以在列表的每一项中增加一个role字段,这样在record中都存在一个最新的role值,但是这样的缺点是会创建多余的变量以及消耗内存空间

2、返回列表页面,接口重新调用,数据已更新为“待付款”,页面也已经刷新,为什么不显示倒计时?

原因:

当调用浏览器的“返回”操作后,如果页面URL参数接口参数都没有变化的时候,浏览器默认认为内容没有任何变化,此时即使浏览器重新刷新,接口重新调用,浏览器也会直接拿缓存中的数据进行渲染,因为这样速度很快,所以出现了不展示倒计时的情况。

解决方法:

在接口请求后面增加随机参数_v=Math.randow()或者_v=Date.now()

详细解释及说明可以参考:# 浏览器缓存之返回上一级页面,数据不刷新

3、倒计时为什么console的数据在实时改变,然后页面数据却没有变化?

原因:

useEffect内部不能修改state

详细原因及解决方法可以参考:# React Hooks实现倒计时及避坑

更新 Loading....

之后在工作中遇到各种“坑”都会及时更新的,也欢迎各位分享自己遇到的“奇葩事”~