Next.js で TanStack Table v8 を使用する場合の描画ループに対処してみた
こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。
Next.jsでTanStack Table v8を使用した際に、以下のような描画ループに関するメッセージが発生して困っていました。
今回は、この事象への対応について書きたいと思います。
そもそもどんなエラーなのか
エラーメッセージを日本語にすると、以下のようなメッセージとなります。
一方で、今回この事象が発生したコードでは useEffect は利用していないので、似通った問題に当たりを付けて原因を探ってみました。
事象が発生したコードについて
今回、事象が発生したコードは、以下のサンプルを元に作成したコードでした。
1つ違うポイントとしては、テーブルに利用するデータはサンプルのダミーデータではなく実データとなるので、SWRを利用して取得しています。
そのため、該当コードは以下のようなイメージになります。
function Profile() { const { data, error, isLoading } = useSWR('/api/user', fetcher) // TanStack Table 用の設定、準備をする xxxxx if (error) return <div>failed to load</div> if (isLoading) return <div>loading...</div> return ( <div> <!-- TanStack Tableを利用してテーブルを描画する --> xxxxx </div> ) }
isLoading
が true
の場合はローディング表示をして、ロードが完了したらテーブルを描画したいです。
エラーの発生原因を特定する
エラーのスタックトレースとコードをにらめっこしたところ、以下が原因となって描画ループが発生していました。
また、サンプルコードにはありませんが table.getSelectedRowModel().rows
を利用している箇所でも発生していました。
エラーに対応してみた
table.getRowModel().rows と table.getSelectedRowModel().rows
描画ループが発生している = メモ化をすべき と考え、以下のようにuseMemo
を利用してメモ化を行いました。
const selectedRows: Row<XXXXX>[] = useMemo(() => { return table.getSelectedRowModel().rows }, [table, table.getIsSomeRowsSelected(), table.getIsAllRowsSelected()]) const rows: Row<XXXXX>[] = useMemo(() => { return table.getRowModel().rows }, [table, isLoading, globalFilter])
Row[]
XXXXXの型には各rowで利用しているデータ型を適宜しています。useReactTableで、data
として指定するデータの型ですね。
selectedRows
ここでは依存関係として、table
の他に明示的にtable.getIsSomeRowsSelected()
とtable.getIsAllRowsSelected()
を指定しています。
これにより、テーブルでチェックボックスの選択状態が変わった時に、選択中の行であるtable.getSelectedRowModel().rows
を更新するようにしました。
rows
ここでは依存関係として、table
の他に明示的にisLoading
とglobalFilterを指定しています。
これにより、データロードが完了した時と、フィルタ条件が変わったときに、テーブルの表示行であるtable.getRowModel().rows
を更新するようにしました。
table.getIsAllRowsSelected()
ここについては、上手い解決方法が見つかっていません。
実際の利用箇所は以下のようなイメージとなります。
const columns = React.useMemo<ColumnDef<Person>[]>( () => [ { id: 'select', header: ({ table }) => ( <IndeterminateCheckbox {...{ checked: table.getIsAllRowsSelected(), (...略...) ], [] ) const table = useReactTable({ data, columns, (...略...) })
ここで、table.getIsAllRowsSelected()
をメモ化しようとすると、必然的にcolumns
のコードの前でconst定義をすることになります。
一方で、その時点ではtable
の定義がされていないので、依存関係の定義がうまくできずに「鶏が先か、卵が先か」のようなジレンマが発生してしまいます。
もし良い方法を思いついたら、また追記をしたいと思います。
まとめ
以上、Next.js で TanStack Table v8 を使用する場合の描画ループに対処してみました。
Next.js を利用していると割と良く描画ループ問題に直面するので、今回のメモ化での対応も含めて対応方法を忘れないようにしたいと思います。
どなたかのお役に立てば幸いです。それでは!