irychen / keepalive-for-react

React KeepAlive is a component that can cache the state of the component and reuse it when needed.
MIT License
187 stars 34 forks source link

未保存滚动条位置,如果table有虚拟滚动的话,会导致数据空白 #12

Closed jinxiangqiang closed 4 months ago

irychen commented 4 months ago

嗯 因为table的虚拟滚动没有 设置复位

import { Table } from "antd"
import { useEffect, useRef, useState } from "react"
import { useKeepAliveContext } from "keepalive-for-react"

function TablePage() {
    const divRef = useRef<HTMLDivElement>(null)
    const tableRef = useRef<HTMLDivElement>(null)
    const historyTop = useRef(0)
    const [tableY, setTableY] = useState(0)

    const [mounted, setMounted] = useState(false)

    //  10000 条数据
    const dataSource = Array.from({ length: 10000 }, (_, index) => ({
        key: index,
        name: `Edward King ${index}`,
        entryTime: `2021-09-${index}`,
        sex: index % 2 === 0 ? "男" : "女",
        age: 32,
        address: `London, Park Lane no. ${index}`,
        job: "前端工程师",
        phone: "1234567890",
    }))

    const { active } = useKeepAliveContext()

    useEffect(() => {
        if (tableRef.current && active) {
            tableRef.current?.scrollTo({
                top: historyTop.current,
            })
        }
    }, [active])

    useEffect(() => {
        const div = divRef.current
        if (div) {
            const resize = () => {
                const { height } = div.getBoundingClientRect() || {}
                setTableY(height)
            }
            window.addEventListener("resize", resize)
            resize()
            return () => window.removeEventListener("resize", resize)
        }
    }, [setTableY, active])

    useEffect(() => {
        setMounted(true)
    }, [])

    return (
        <div ref={divRef} className={"w-full h-full p-[8px]"}>
            {mounted && (
                <Table
                    ref={tableRef as any}
                    size={"small"}
                    virtual
                    onScroll={e => {
                        const target = e.target as HTMLDivElement
                        if (!target) return
                        historyTop.current = target?.scrollTop || 0
                    }}
                    rowKey={row => row.name}
                    dataSource={dataSource}
                    scroll={{ x: 2000, y: tableY - 54 }}
                    pagination={false}
                    columns={[
                        {
                            title: "姓名",
                            align: "center",
                            dataIndex: "name",
                            key: "name",
                        },
                        // 入职时间
                        {
                            title: "入职时间",
                            align: "center",
                            dataIndex: "entryTime",
                            key: "entryTime",
                            render: (_, row) => <span>{row.entryTime}</span>,
                        },
                        {
                            title: "性别",
                            align: "center",
                            dataIndex: "sex",
                            key: "sex",
                        },
                        {
                            title: "年龄",
                            align: "center",
                            dataIndex: "age",
                            key: "age",
                        },
                        {
                            title: "地址",
                            align: "center",
                            dataIndex: "address",
                            key: "address",
                        },
                        {
                            title: "工作",
                            align: "center",
                            dataIndex: "job",
                            key: "job",
                        },
                        {
                            title: "电话",
                            align: "center",
                            dataIndex: "phone",
                            key: "phone",
                        },
                    ]}
                ></Table>
            )}
        </div>
    )
}

export default TablePage
irychen commented 4 months ago

还有需要挂到body的antd组件 需要重新挂在到 缓存节点内 不然suspense会出现闪烁 例如 Select Tooltip DatePicker ...

解决办法

<Select
  getPopupContainer={triggerNode => triggerNode.parentNode}
 ....
/>

其他组件类似

irychen commented 4 months ago

虚拟列表复位例子 https://super-admin.tech/#/virtual-table 需要先登陆 https://super-admin.tech/#/login

irychen commented 4 months ago

antd procomponents 的 Protable 没有提供 tableRef.current?.scrollTo 所以在 super admin 里面我自己封装了一个类似组件

https://github.com/irychen/super-admin/blob/main/src/components/SuperTable/index.tsx

jinxiangqiang commented 4 months ago

我之前用的 umi-plugin-keep-alive ,是老项目改造,这样的话改动会比较大,可以提供类似saveScrollPosition这个API吗