Avoiding 'Rendered more hooks than during the previous render' on button-ran application

Hello! I’m writing a custom app to print information from my base on adhesive labels using a Zebra TLP2824 Plus printer, via Zebra Browser print.

I’ve written the script below to gather, format and print the required information, but my amateur at best programming knowledge is holding me back a little bit.

The functionality I’m going for is the following: once a button in an Airtable view is pressed, labels print for all records linked to a parent record. This works when the app is loaded from the button itself, but if the app loads without the useRecordActionData already loaded, I can’t get it to load without it throwing the following error:

Rendered more hooks than during the previous render.

Which is obviously right, because I’m accessing more data than requested initially, but I can’t figure out how to work around this!

I’d really appreciate some help. Thanks!

import {initializeBlock, useBase, useRecords, useRecordById, useRecordActionData} from '@airtable/blocks/ui';
import ZebraBrowserPrintWrapper from 'zebra-browser-print-wrapper';
import React from 'react';


const printBarcode = async (serial) => {
    try {

        // Create a new instance of the object
        const browserPrint =  new ZebraBrowserPrintWrapper();

        // Select default printer
        const defaultPrinter =  await browserPrint.getDefaultPrinter();
    
        // Set the printer
        browserPrint.setPrinter(defaultPrinter);

        // Check printer status
        const printerStatus = await browserPrint.checkPrinterStatus();

        // Check if the printer is ready
        if(printerStatus.isReadyToPrint) {


        browserPrint.print(serial);

        } else {
        console.log("Error/s", printerStatus.errors);
        }

    } catch (error) {
        throw new Error(error);
    }
};

function getRecordCellString(recordID, cell){
        
    const base = useBase();
    const table = base.getTableByName('Role');
    const record = useRecordById(table, recordID);

    return record.getCellValueAsString(cell);


}

function getRecordCell(recordID, cell){
    
    const base = useBase();
    const table = base.getTableByName('Role');
    const record = useRecordById(table, recordID);

    return record.getCellValue(cell);


}

function FormatZPL(serial){

    const twincheck = getRecordCellString(serial,'Twincheck');
    const serviciu = getRecordCellString(serial,'Serviciu').replace(/[^A-Z0-9]/ig, "").toUpperCase();
    const addons = getRecordCellString(serial,'Addons').split(', ').join('\\&').toUpperCase();
    const zpl = `
                ^XA
                ^FB119,1,,C,^A0R,50,50^FD${twincheck}\\&^FS
                ^FO40,0
                ^FB119,7,,C,^A0R,25,25^FD${serviciu}\\&${addons}\\&
                ^PQ2
                ^XZ
                `;

    return zpl;
}


function ZebraTwincheckPrinting() {
    const actionData = useRecordActionData();
    
    if (actionData === null) {
        return <span>No events yet</span>;
    }
    
    var serial = actionData.recordId;

    var zplToPrint = '';
    var LinkedRecordArray = getRecordCell(serial, 'OtherRollsInOrder');
  
    LinkedRecordArray.forEach(function (element){
        let recordToPrint = element["value"]["id"];
        zplToPrint += FormatZPL(recordToPrint);
    }); 

    printBarcode(zplToPrint); 
    
    return `Printed ${zplToPrint}`; 
}



initializeBlock(() => <ZebraTwincheckPrinting />);

Edit: One more thing - the data in the app automatically updates when a record is edited. This is not intended functionality - I’d like for the data to get pulled from the airtable records only when the button is pressed and remain static until the button is pressed again.

The “hooks” are the Airtable elements that have “use” in front of them: useBase, useRecords, useRecordById, useRecordActionData. These hooks need to be at the “top-level” and always run with every render of the the component that they are in. These hooks are also why your app is re-rendering when a record is edited.

It looks like you are trying to do everything in one component. You are also calling the hooks within nested functions.

While the concept behind the error is fairly straightforward, re-writing the code to fix this error can be hard to do when you are new to React. Usually I recommend an approach that involves using multiple components, which has its own learning curve.

On the other hand, you probably only need the “useBase” and “useRecordActionData” hook, and not the other hooks, since you do not want the app to re-render when the record changes.

However, things get even more complicated when you want the app to re-render with every button press. The “useRecordActionData” hook will cause your app to re-render every time you press a new button, but getting it to re-render if you press the same button multiple times in a row is more difficult. This post has more info.

Thank you @kuovonne! I’d figured hooks weren’t the right tool to use for accessing the record data, as I didn’t need the data to update dynamically.

Which method should I use to access the data statically instead of hooks?

selectRecordsAsync

1 Like

Thank you! I’ll give it a shot and hope it works! :crossed_fingers:

This topic was solved and automatically closed 3 days after the last reply. New replies are no longer allowed.