ambitious-aqua
ambitious-aqua2mo ago

Lazy loading a nested Ant Design table

I'm trying to lazy load a nested table but running into trouble since if I call useTable in the function passed into onExpand, I get an error saying that React hooks always need to be called in the same order. I've also tried to use a simple API request to the endpoint I need but I'm missing auth headers since the request isn't being made by my data provider. Here's some context about my architecture: I have a resource called "PublishResults" with two foreign key attributes, "draft_event_id" , a FK to the "DraftEvents" table and "latest_request_id", which is a FK to the RequestHistory table. Also, the only connection between the RequestHistory and the DraftEvent table is that there's a key within RequestHistory's jsonb notes column called "draft_event_id". In addition, there may be multiple RequestHistory records per one DraftEvent. Here's what I want to do: I'm displaying a table of PublishResults in refine using the useTable hook and I want to show all of the RequestHistory records in the nested table that relate to the "draft_event_id" of the PublishResult record. I only want to request the data necessary for the nested table if the user expands the row. How do I do this?
2 Replies
kapa.ai
kapa.ai2mo ago
Hi @srsoccer09. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You're correct that calling React hooks like useTable inside a callback (such as onExpand) will violate the rules of hooks, leading to errors. This is a common challenge when implementing lazy-loaded nested tables in React, especially with Refine and Ant Design. Recommended Approach 1. Use Local State to Track Expanded Rows and Data Instead of calling useTable inside onExpand, you should: - Use useTable for your main (parent) table as usual. - Track which rows are expanded in local state. - When a row is expanded, trigger a data fetch for the nested table using a data fetching hook that can be called imperatively (not as a React hook), or use a stateful approach to trigger a fetch. 2. Fetch Nested Data on Demand Since you want to use your data provider (to include auth headers, etc.), you can use the useCustom hook from Refine, which allows you to fetch data imperatively and is suitable for this use case. You can call its refetch method when a row is expanded, and store the result in local state keyed by the parent row's ID. 3. Example Pattern While the knowledge sources do not provide a full code example for this exact scenario, they do describe using useCustom for imperative data fetching and how to manage state for such cases. Here’s a pattern based on the guidance from the knowledge sources:
import React, { useState } from "react";
import { useTable, useCustom } from "@refinedev/core";
import { Table } from "antd";

const PublishResultsTable = () => {
const { tableProps } = useTable({ resource: "publishResults" });
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const [nestedData, setNestedData] = useState({});
// This is a placeholder for the useCustom hook, which you can use to fetch nested data.
// You can also use useCustom for each expanded row, or manage a map of useCustom hooks.
// For simplicity, we'll use a function to fetch data on demand.

const fetchRequestHistory = async (draft_event_id) => {
// Use your dataProvider's getList or useCustom here
// Example:
const { data } = await dataProvider.getList({
resource: "requestHistory",
filters: [
{
field: "notes.draft_event_id",
operator: "eq",
value: draft_event_id,
},
],
// ...other params as needed
});
setNestedData((prev) => ({ ...prev, [draft_event_id]: data }));
};

const handleExpand = async (expanded, record) => {
if (expanded && !nestedData[record.draft_event_id]) {
await fetchRequestHistory(record.draft_event_id);
}
setExpandedRowKeys((prev) =>
expanded
? [...prev, record.key]
: prev.filter((key) => key !== record.key)
);
};

return (
<Table
{...tableProps}
expandable={{
expandedRowKeys,
onExpand: handleExpand,
expandedRowRender: (record) => (
<Table
dataSource={nestedData[record.draft_event_id] || []}
// ...columns and other props
/>
),
}}
/>
);
};
import React, { useState } from "react";
import { useTable, useCustom } from "@refinedev/core";
import { Table } from "antd";

const PublishResultsTable = () => {
const { tableProps } = useTable({ resource: "publishResults" });
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const [nestedData, setNestedData] = useState({});
// This is a placeholder for the useCustom hook, which you can use to fetch nested data.
// You can also use useCustom for each expanded row, or manage a map of useCustom hooks.
// For simplicity, we'll use a function to fetch data on demand.

const fetchRequestHistory = async (draft_event_id) => {
// Use your dataProvider's getList or useCustom here
// Example:
const { data } = await dataProvider.getList({
resource: "requestHistory",
filters: [
{
field: "notes.draft_event_id",
operator: "eq",
value: draft_event_id,
},
],
// ...other params as needed
});
setNestedData((prev) => ({ ...prev, [draft_event_id]: data }));
};

const handleExpand = async (expanded, record) => {
if (expanded && !nestedData[record.draft_event_id]) {
await fetchRequestHistory(record.draft_event_id);
}
setExpandedRowKeys((prev) =>
expanded
? [...prev, record.key]
: prev.filter((key) => key !== record.key)
);
};

return (
<Table
{...tableProps}
expandable={{
expandedRowKeys,
onExpand: handleExpand,
expandedRowRender: (record) => (
<Table
dataSource={nestedData[record.draft_event_id] || []}
// ...columns and other props
/>
),
}}
/>
);
};
Key Points: - Do not call useTable or any React hook inside onExpand or expandedRowRender. - Use useCustom or your dataProvider directly to fetch nested data on demand. - Store the nested data in local state, keyed by the parent row's identifier. - Pass the fetched data to the nested table in expandedRowRender. References: - The useCustom pattern for imperative data fetching is described in this GitHub issue comment. - The general approach to handling nested resources and dynamic API paths is discussed here. If you need to include auth headers or other meta information, you can use the meta property with useCustom or your dataProvider methods as described in the meta documentation. --- Summary:
You cannot use useTable inside onExpand due to React's rules of hooks. Instead, use local state and imperative data fetching (e.g., useCustom or dataProvider.getList) to fetch and display nested data when a row is expanded, ensuring all requests go through your data provider for proper authentication and configuration.
If you need a more detailed code example or further clarification, let me know! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@200748253757702145> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?