React and the state

I have added a form field to my custom block and I am updating its value via the React state. It works well and looks fast.

The block is fairly standard. It reads values from a table, filters them and then displays them in a list.

When I look at the console I see my logs telling how many rows were read after every character entered into the input field.This means all the code of my block (inside index.js) is executed again and again. There is a useRecords statement just before this log output.

Does this mean the rows are read from Airtable for every character pressed (round trip every time)? Or is there some form of caching?

Will I get a performance hit when there are many (1000+) rows in the table?

Hi Chris,

The rows are not read on each keypress. Updating the input updates state, which causes the block to re-render. The component’s code is executed on each render as it’s a React function component, so that’s expected.

I can’t say for sure without seeing the code, but my guess is that your logging is occurring on each render. If it’s something like const records = useRecords(table); console.log(records.length), this is showing how many records there are on each render, not that that many records were just loaded.

Billy’s response about when new renders occur provides more details: AMA Custom Blocks Contest Edition #3 - Releasing your block

Regarding record data loading: the SDK handles loading the data on demand (e.g. component using useRecords is mounted), listening to updates coming from Airtable + notifying you of updates, and unloading it when no longer required (e.g. the component using useRecords unmounts). Since it’s loading all of this data and holding it in memory, performance will degrade if there are lots of records and/or fields. To address this, we recommend using RecordQueryResultOpts to only load the data for the fields you need.

1 Like

OK So here is how I understand it: The actual reading of the records happens in the React component which mirrors somehow an Airtable “middleware” object that gives me the data (all rows for the table) and updates the state of the data as needed so that the React object can re-render and show me the updated data. I can specify a sub-set of columns but not of rows, so I always get all rows.

So this leads me to another question. How much data is actually brought “down the wire” and when? If I look at Airtable, there is one tab per table and all the rows of a table showing on a tab. If a table has links to say 5 other tables, is the all the data of those other five tables coming into the React component? How does Airtable then handle large datasets. Let us say I have a list of 5000 materials and I have a works order table that can use any one of those materials, are all 5000 materials also loaded?

In the api model there is the concept of a cursor and one actually pulls just the rows and columns one needs, 100 at a time. But it looks to me like that is not the case with custom blocks.

Hi Chris,

Will respond to your message in parts as there’s a lot to unpack here!

OK So here is how I understand it: The actual reading of the records happens in the React component which mirrors somehow an Airtable “middleware” object that gives me the data (all rows for the table) and updates the state of the data as needed so that the React object can re-render and show me the updated data.

Essentially, yes. To go into specifics, what useRecords does under the hood is:

  • Get a QueryResult for the table/view
  • Load the QueryResult using useLoadable.
    • This calls queryResult.loadDataAsync() and put the block into React suspense (shows a loading spinner) while the data is loading asynchronously
    • This also registers the block to be kept “up to date” on the data (i.e. will receive updates to the data)
  • Watch the QueryResult using useWatchable
    • This will re-render the block whenever the data updates.

You can see more details in the source code for useRecords and in the guide about reading data in a custom block. useLoadable, useWatchable and queryResult can all be used directly, but we generally recommend using the higher level hooks as they’re easier to use.

I can specify a sub-set of columns but not of rows, so I always get all rows.

This is correct

So this leads me to another question. How much data is actually brought “down the wire” and when?

It depends what you mean by “the wire”. Blocks run in the context of an Airtable base, where the table data is actually already loaded into your browser. When we say the data is asynchronously being loaded into the block, that means it’s being loaded from the main Airtable window into the block’s iframe.

If I look at Airtable, there is one tab per table and all the rows of a table showing on a tab. If a table has links to say 5 other tables, is the all the data of those other five tables coming into the React component? How does Airtable then handle large datasets. Let us say I have a list of 5000 materials and I have a works order table that can use any one of those materials, are all 5000 materials also loaded?

When using linked records, you need to load the data of the linked records separately by getting the LinkedRecordsQueryResult from record.selectLinkedRecordsFromCell and loading it.

(So, to answer your question more directly: if you’re only doing useRecords(mainTable) or similar, that only loads/watches mainTable and not the linked tables. You can see what’s loaded for the linked record cells by referring to the cell read format here - it’s just the name and id.)

In the api model there is the concept of a cursor and one actually pulls just the rows and columns one needs, 100 at a time. But it looks to me like that is not the case with custom blocks.

That’s correct, there is no pagination within the current custom blocks APIs.

Hope that helps!

1 Like

Thank you for a thorough reponse.

1 Like