Feb 07, 2022 04:46 AM
My app is reading data from multiple tables, then displays them in context and interconnected. To be able to react to user changes I’m using the useRecords hook, so that updates propagate to the app.
When searching why the app loads so slowly I realized that it starts over and over again, with each useRecords hook. Is there a way/pattern to avoid this, while still being able to read all the necessary table data?
My code:
...
const base = useBase();
// get all necessary Airtable tables
const tables = React.useMemo(() => {
return {
productTable: base.getTableByNameIfExists(Constants.PRODUCTS_TABLE_NAME),
costTable: base.getTableByNameIfExists(Constants.COST_TABLE_NAME),
contactsTable: base.getTableByNameIfExists(Constants.CONTACTS_TABLE_NAME),
conversionTable: base.getTableByNameIfExists(Constants.CONVERSIONS_TABLE_NAME),
contributorTable: base.getTableByNameIfExists(Constants.CONTRIBUTORS_TABLE_NAME),
};
}, [base]);
console.log('tables have been connected...')
const conversions = useRecords(tables.conversionTable);
console.log('conversions data has been read...')
const contributors = useRecords(tables.contributorTable);
console.log('contributor data has been read...')
const costs = useRecords(tables.costTable);
console.log('tracker data has been read...')
const products = useRecords(tables.productTable);
console.log('product data has been read...')
const vendors = useRecords(tables.contactsTable);
console.log('vendor data has been read...')
...
Console Output:
tables have been connected...
...
tables have been connected...
conversions data has been read...
...
tables have been connected...
conversions data has been read...
contributor data has been read...
...
tables have been connected...
conversions data has been read...
contributor data has been read...
tracker data has been read...
...
tables have been connected...
conversions data has been read...
contributor data has been read...
tracker data has been read...
product data has been read...
...
tables have been connected...
conversions data has been read...
contributor data has been read...
tracker data has been read...
product data has been read...
vendor data has been read...
...
Solved! Go to Solution.
Feb 23, 2022 12:27 PM
Hi Michael,
As you point out, the useRecords() hook will update the component used by your app with the underlying data changes to records, but it also handles loading data automatically.
Within the useRecords() hook we use suspense while loading data so that downstream logic does not get called until the data has finished loading. As you see from your console output, this causes the component to be re-rendered with each call of useRecords() before the next line can be executed.
You might be able to reduce some of the slowness your app is experiencing by starting the data load outside of the useRecords() call which uses the suspended loads. loadDataAsync() will allow you to do this asynchronously for each table and can be called from a query result, such as the one returned by table.selectRecords(). That way, the data for each table starts loading before suspense is used via useRecords().
You’ll still see the component re-render once the data for each is finished loading, but since the loading will now happen asynchronously, this should cut out some of the overall load time. Here’s what that might look like:
const base = useBase();
// get all necessary Airtable tables
const tables = React.useMemo(() => {
return {
productTable: base.getTableByNameIfExists(Constants.PRODUCTS_TABLE_NAME),
costTable: base.getTableByNameIfExists(Constants.COST_TABLE_NAME),
contactsTable: base.getTableByNameIfExists(Constants.CONTACTS_TABLE_NAME),
conversionTable: base.getTableByNameIfExists(Constants.CONVERSIONS_TABLE_NAME),
contributorTable: base.getTableByNameIfExists(Constants.CONTRIBUTORS_TABLE_NAME),
};
}, [base]);
console.log('tables have been connected...')
const productTableQueryResult = tables.productTable.selectRecords()
const costTableQueryResult = tables.costTable.selectRecords()
const contactsTableQueryResult = tables.contactsTable.selectRecords()
const conversionTableQueryResult = tables.conversionTable.selectRecords()
const contributorTableQueryResult = tables.contributorTable.selectRecords()
React.useEffect( () => {
productTableQueryResult.loadDataAsync()
costTableQueryResult.loadDataAsync()
contactsTableQueryResult.loadDataAsync()
conversionTableQueryResult.loadDataAsync()
contributorTableQueryResult.loadDataAsync()
return () => {
productTableQueryResult.unloadData()
costTableQueryResult.unloadData()
contactsTableQueryResult.unloadData()
conversionTableQueryResult.loadDataAsync()
contributorTableQueryResult.loadDataAsync()
}
}, [])
const conversions = useRecords(tables.conversionTable);
console.log('conversions data has been read...')
const contributors = useRecords(tables.contributorTable);
console.log('contributor data has been read...')
const costs = useRecords(tables.costTable);
console.log('tracker data has been read...')
const products = useRecords(tables.productTable);
console.log('product data has been read...')
const vendors = useRecords(tables.contactsTable);
console.log('vendor data has been read...')
Let us know if that works for you.
Feb 21, 2022 06:59 AM
It seems as if nobody has encountered (or esolved) this before? A cross-posting at Stackoverflow has not yielded any insights either.
Feb 23, 2022 12:27 PM
Hi Michael,
As you point out, the useRecords() hook will update the component used by your app with the underlying data changes to records, but it also handles loading data automatically.
Within the useRecords() hook we use suspense while loading data so that downstream logic does not get called until the data has finished loading. As you see from your console output, this causes the component to be re-rendered with each call of useRecords() before the next line can be executed.
You might be able to reduce some of the slowness your app is experiencing by starting the data load outside of the useRecords() call which uses the suspended loads. loadDataAsync() will allow you to do this asynchronously for each table and can be called from a query result, such as the one returned by table.selectRecords(). That way, the data for each table starts loading before suspense is used via useRecords().
You’ll still see the component re-render once the data for each is finished loading, but since the loading will now happen asynchronously, this should cut out some of the overall load time. Here’s what that might look like:
const base = useBase();
// get all necessary Airtable tables
const tables = React.useMemo(() => {
return {
productTable: base.getTableByNameIfExists(Constants.PRODUCTS_TABLE_NAME),
costTable: base.getTableByNameIfExists(Constants.COST_TABLE_NAME),
contactsTable: base.getTableByNameIfExists(Constants.CONTACTS_TABLE_NAME),
conversionTable: base.getTableByNameIfExists(Constants.CONVERSIONS_TABLE_NAME),
contributorTable: base.getTableByNameIfExists(Constants.CONTRIBUTORS_TABLE_NAME),
};
}, [base]);
console.log('tables have been connected...')
const productTableQueryResult = tables.productTable.selectRecords()
const costTableQueryResult = tables.costTable.selectRecords()
const contactsTableQueryResult = tables.contactsTable.selectRecords()
const conversionTableQueryResult = tables.conversionTable.selectRecords()
const contributorTableQueryResult = tables.contributorTable.selectRecords()
React.useEffect( () => {
productTableQueryResult.loadDataAsync()
costTableQueryResult.loadDataAsync()
contactsTableQueryResult.loadDataAsync()
conversionTableQueryResult.loadDataAsync()
contributorTableQueryResult.loadDataAsync()
return () => {
productTableQueryResult.unloadData()
costTableQueryResult.unloadData()
contactsTableQueryResult.unloadData()
conversionTableQueryResult.loadDataAsync()
contributorTableQueryResult.loadDataAsync()
}
}, [])
const conversions = useRecords(tables.conversionTable);
console.log('conversions data has been read...')
const contributors = useRecords(tables.contributorTable);
console.log('contributor data has been read...')
const costs = useRecords(tables.costTable);
console.log('tracker data has been read...')
const products = useRecords(tables.productTable);
console.log('product data has been read...')
const vendors = useRecords(tables.contactsTable);
console.log('vendor data has been read...')
Let us know if that works for you.
Feb 24, 2022 09:00 AM
Thanks a lot, will give this a try!