React hooks error when building Airtable block extension

I keep running into this React error while working on my Airtable block:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

I’m building a filter system that uses a select dropdown to update records in real time. The component has useState for managing the selected value and useEffect for database updates. Both hooks are placed correctly inside my React component, so I can’t figure out what’s wrong. Could this be related to version mismatches between React libraries?

import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import {
    Select,
    initializeBlock,
    useBase,
    useRecords,
    FormField,
    Box,
} from '@airtable/blocks/ui';

const database = useBase();
const dataTable = database.getTable("Project Tasks");

export default function TaskFilter() {
    let taskRecords = useRecords(dataTable);
    
    var projectIds = [];
    
    const [selectedId, setSelectedId] = useState("");
    
    const query = dataTable.selectRecords({ fields: ["Project ID"] });
    
    taskRecords.forEach(function (record) {
        if (projectIds.indexOf(record.getCellValueAsString("Project ID"), -1)) {
            projectIds.push({ 
                value: record.getCellValueAsString("Project ID"), 
                label: record.getCellValueAsString("Project ID") 
            });
        }
    });
    
    query.unloadData();
    
    let recordUpdates = [];
    
    useEffect(() => {
        const updateRecords = async () => {
            taskRecords.forEach(function (record) {
                if (record.getCellValueAsString('Project ID') == selectedId) {
                    recordUpdates.push({ 
                        id: record.id, 
                        fields: { 'Is Active': true } 
                    });
                }
                else if (record.getCellValueAsString('Project ID') !== selectedId && 
                        record.getCellValueAsString('Is Active') == 'checked') {
                    recordUpdates.push({ 
                        id: record.id, 
                        fields: { 'Is Active': false } 
                    });
                }
            });
            
            while (recordUpdates.length) {
                await dataTable.updateRecordsAsync(recordUpdates.splice(0, 50));
            }
        }
        
        updateRecords().catch(console.error);
    }, [selectedId])
    
    return (
        <div>
            <FormField label="Select Project">
                <Select
                    options={projectIds}
                    value={selectedId}
                    onChange={newId => setSelectedId(newId.toString())}
                    width="300px"
                />
            </FormField>
        </div>
    );
}

It seems you might be using your hooks incorrectly. Both useBase() and useRecords() should be called within your TaskFilter component instead of at the top level. React enforces certain rules for hooks, and calling them outside a functional component can lead to the error you’re experiencing. Simply move these function calls into the body of your TaskFilter function to resolve the issue. This way, your hooks will be correctly aligned with React’s rules.

Yeah, same issue here. Your useBase() call is outside the component function, which breaks React’s hook rules. Move it inside TaskFilter with the table reference and you’ll be good to go.