React-Table 导致无限重新渲染

问题描述 投票:0回答:2

我最近开发了一个带有表格的组件,并决定是时候尝试一下

react-table
包了。

以下组件(我知道它很大,是一个用于测试目的的组件)导致无限调用

getRowModel
。我还添加了一个
useEffect
来跟踪与
react-table
无关的重新渲染。

//flows.tsx
import { RouteGuard } from '@/components/utils/guards/RouteGuard';
import { useFullFlows } from '@/hooks/flows/useFullFlows';
import { Flow } from '@/shared/models/flows/flow';
import { Suspense, useEffect, useMemo, useState } from 'react';
import {
    SortingState,
    createColumnHelper,
    flexRender,
    getCoreRowModel,
    getSortedRowModel,
    useReactTable,
} from '@tanstack/react-table';
import { FlowsPageHeader } from '../components/flows/view/FlowsPageHeader';
import moment from 'moment';
import Head from 'next/head';

const count = 50;

const columnHelper = createColumnHelper<Flow>();

export default function Workflows() {
    const columns = useMemo(
        () => [
            columnHelper.accessor('name', {
                header: 'Flow name',
                sortingFn: 'alphanumeric',
            }),
            columnHelper.accessor('updatedAt', {
                header: 'Modified',
                cell: ({ cell: { getValue } }) =>
                    moment(getValue() as Date)
                        .local()
                        .format('DD.MM.YYYY'),
            }),
            columnHelper.display({
                header: 'Preview',
                cell: () => <Button size='sm' />,
            }),
            columnHelper.display({
                header: 'Edit',
                cell: () => <Button size='sm' />,
            }),
            columnHelper.display({
                id: 'actions',
                cell: () => (
                    <>
                        <Button></Button>
                        <Button></Button>
                        <Button></Button>
                        <Button></Button>
                    </>
                ),
            }),
        ],
        []
    );

    const [page, setPage] = useState(0);

    const { flows } = useFullFlows({ page, count: 50 });

    useEffect(() => console.log('first'));

    const { getHeaderGroups, getRowModel } = useReactTable<Flow>({
        columns,
        data: flows,
        getCoreRowModel: getCoreRowModel(),
        debugTable: process.env.NODE_ENV === 'development',
    });

    return (
        <RouteGuard>
            <Head>
                <title>Flows</title>
            </Head>
            <FlowsPageHeader />
            <div className='flex flex-col m-8 rounded-lg shadow-around'>
                <div className='flex justify-between px-6 py-4'>
                    <div className='flex items-center gap-4'>
                        <Checkbox size='sm' className='mr-2 rounded-sm' />
                        <Dropdown>
                            <Dropdown.Toggle
                                size='xs'
                                className='[&>button]:rounded-sm'>
                                :
                            </Dropdown.Toggle>
                        </Dropdown>
                        <Input size='xs' className='rounded-sm' />
                    </div>
                    <div className='flex items-center gap-2'>
                        <Button size='xs' shape='square'>
                            {'<'}
                        </Button>
                        <Button size='xs' shape='square'>
                            {'>'}
                        </Button>
                    </div>
                </div>
                <table>
                    <thead>
                        {getHeaderGroups().map(headerGroup => (
                            <tr key={headerGroup.id}>
                                {headerGroup.headers.map(header => (
                                    <th key={header.id}>
                                        {header.isPlaceholder ? null : (
                                            <div
                                                {...{
                                                    className:
                                                        header.column.getCanSort()
                                                            ? 'cursor-pointer select-none'
                                                            : '',
                                                    onClick:
                                                        header.column.getToggleSortingHandler(),
                                                }}>
                                                {flexRender(
                                                    header.column.columnDef
                                                        .header,
                                                    header.getContext()
                                                )}
                                                {{
                                                    asc: ' 🔼',
                                                    desc: ' 🔽',
                                                }[
                                                    header.column.getIsSorted() as string
                                                ] ?? null}
                                            </div>
                                        )}
                                    </th>
                                ))}
                            </tr>
                        ))}
                    </thead>
                    <tbody>
                        {getRowModel().rows.map(row => (
                            <tr key={row.id}>
                                {row.getVisibleCells().map(cell => (
                                    <td key={cell.id}>
                                        {flexRender(
                                            cell.column.columnDef.cell,
                                            cell.getContext()
                                        )}
                                    </td>
                                ))}
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </RouteGuard>
    );
}

代码本质上是来自https://tanstack.com/table/v8/docs/examples/react/basic的示例代码。 当我删除

tbody
内的代码时,问题就停止了。

起初我认为这一定是我的自定义挂钩或

RouteGuard
组件的问题,但我检查了使用它们的其他组件中的重新渲染问题,并没有发现任何问题。

无论如何,我将在此处包含它们的代码:

使用FullFlows:

//useFullFlows.ts
import { useMemo } from 'react';
import { useFlows } from './useFlows';
import { getFlow } from '@/shared/api/flow';
import { useQueries } from '@tanstack/react-query';
import { Flow } from '@/shared/models/flows/flow';

export function useFullFlows({ page, count }: { page: number; count: number }) {
    const { flows, ...query } = useFlows();

    const fullFlows = useQueries({ queries: flows?.slice(page * count, (page + 1) * count).map(flow => ({
        queryFn: () => getFlow(flow._id),
        queryKey: ['flow', flow._id],
    })) ?? [] });

    return useMemo(() => ({
        flows: fullFlows
            .filter(query => query.data?.flow !== undefined)
            .map(query => query.data?.flow as Flow),
            ...query
    }), [fullFlows, query]);
}

使用流程

//useFlows.ts
import { getAllFlows } from '@/shared/api/flow';
import { useQuery } from '@tanstack/react-query';
import { useActiveWorkspace } from '../workspaces/useActiveWorkspace';

export function useFlows() {
    const {workspace} = useActiveWorkspace()
    const { data, ...query } = useQuery(['flows', workspace?._id], () =>
        getAllFlows(workspace?._id ?? ''),
        {enabled: workspace?._id ? true : false}
    );

    return {flows: data?.flows, ...query};
}

使用ActiveWorkspace

//useActiveWorkspace.ts
import { useUserStore } from '@/shared/store/userStore';
import { useEffect, useMemo } from 'react';
import { useWorkspaces } from './useWorkspaces';

export function useActiveWorkspace() {
    const { workspaces, ...query } = useWorkspaces();
    const {
        activeWorkspace,
        actions: { setActiveWorkspace },
    } = useUserStore();

    useEffect(() => {
        if (activeWorkspace) return;
        setActiveWorkspace(workspaces?.[0]._id ?? '');
    }, [activeWorkspace, setActiveWorkspace, workspaces]);

    return useMemo(() => {
        return {
            workspace: workspaces?.find(
                workspace => workspace._id === activeWorkspace
            ),
            ...query,
        };
    }, [activeWorkspace, query, workspaces]);
}

使用工作区

import { useMemo } from 'react';
import { useUserInfo } from '../user/useUserInfo';

export function useWorkspaces() {
    const { userInfo, ...query } = useUserInfo()

    return useMemo(() => ({workspaces: userInfo?.workspaces, ...query}), [query, userInfo?.workspaces]);
}

路线卫士

//RouteGuard.tsx
import { useUserStore } from '@/shared/store/userStore';
import { useRouter } from 'next/router';
import { PropsWithChildren, Suspense, useEffect, useState } from 'react';
import { Sidebar, SidebarOption } from '../../layout/Sidebar';
import { Loader } from '../Loader';
import { FullPageLoader } from '../FullPageLoader';

interface RouteGuardProps extends PropsWithChildren {
    start?: SidebarOption[];
    end?: SidebarOption[];
}

export function RouteGuard({ children, end, start }: RouteGuardProps) {
    const router = useRouter();
    const [authorized, setAuthorized] = useState(false);
    const {
        isAuthenticated,
        actions: { setRedirectPath },
    } = useUserStore();

    useEffect(() => {
        const authCheck = () => {
            if (!isAuthenticated) {
                setAuthorized(false);
                setRedirectPath(router.asPath);
                void router.push({
                    pathname: '/login',
                });
            } else {
                setAuthorized(true);
            }
        };

        authCheck();

        const preventAccess = () => setAuthorized(false);

        router.events.on('routeChangeStart', preventAccess);
        router.events.on('routeChangeComplete', authCheck);

        return () => {
            router.events.off('routeChangeStart', preventAccess);
            router.events.off('routeChangeComplete', authCheck);
        };
    }, [isAuthenticated, router, setRedirectPath]);

    return authorized ? (
        <div className='relative flex w-full h-full overflow-hidden'>
            <Sidebar start={start} end={end} />
            <Suspense fallback={<FullPageLoader />}>
                <div className='w-full h-full overflow-auto'>{children}</div>
            </Suspense>
        </div>
    ) : (
        <div className='flex items-center justify-center w-full h-full'>
            <Loader color='primary' />
        </div>
    );
}

我尝试从方程中删除所有获取的数据并将静态数据传递到

useReactTable
钩子中,但这也不起作用。 唯一停止渲染的两件事是:

  • 从代码中删除
    useFullFlows
    钩子
  • 删除
    tbody
    标签的内容

我自己不知道可能导致问题的原因以及如何继续,主要考虑放弃

react-table
,但我想在此之前尝试获得一些帮助。

网上搜索也没有相关结果。

我希望我的组件仅在状态更改时重新渲染,而不是无明显原因地无限重新渲染。

reactjs next.js infinite-loop react-query react-table
2个回答
0
投票

我在我的桌子上使用

useQuery
时遇到了同样的问题。问题是我在从
[]
拉取时将数据设置为默认
useQuery
:

const {data = []} = useQuery( ... )

解决方法是在我记忆数据的戏剧中执行此逻辑:

const {data: theData} = useQuery( ... )

const data = React.useMemo(() => {
  return Array.isArray(theData) ? theData : [];
}, [theFiles]);

const columns = React.useMemo<ColumnDef<File>[]>(
  () => tableColumns.map(column => COLUMNS[column]),
  [tableColumns]
);

0
投票

尝试用备忘录包装你的组件,这对我很有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.