'Airtable blocks: Multiple useRecords hooks to different tables force repeated app restarts

My blocks custom app is reading data from multiple Airtable tables, then displays them in context and interconnected. To be able to react to user changes I'm using the useRecords hook, so that any user updates also 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 and keep up-to-date all the necessary table data?

My code:

import { initializeBlock, useBase, useRecords } from '@airtable/blocks/ui';
import React from 'react';
...
function MyApp() {
...
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...
...


Solution 1:[1]

For anyone facing the same issue, here FYI the response from Airtable dev support:

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.unloadData()
            contributorTableQueryResult.unloadData()
        }
    }, [])

  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...')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hmec