Search code examples
javascriptreactjsairtable

Javascript : When setState inside promise, getting an unending loop of output


I'm trying to load data from Airtable with the javascript API. But when I try to setState of data inside of a promise block, the promise doesn't 'end'.

Airtable API stuff:

export const listRecords = function() {
  const Airtable = require('airtable');
  const base = new Airtable({ apiKey: apiKey }).base(baseId);

  const list = base(tableName)
  .select({ view: 'Grid view' }).all().then(records => {
    return records;
  }).catch(err => {
    if (err) {
        console.error(err);
        return null;
    }
  });

  return list;
};

Setting state stuff:

const [state, setState] = useState({
    records: []
});

Promise.all([listRecords()]).then((v) => {
    setState((s) => ({ ...s, records: v }));
});

Then when I put console.log before, inside, or after the promise statement, they get called in an unending loop. So my question is, how do I make it so the promise is only called/done once?


Solution

  • If you only want your Promise to run on mount, place this in an effect with an empty dependencies Array:

    function YourComponent(props) {
      const [state, setState] = useState({
        records: []
      });
    
      useEffect(() => {
        Promise.all([listRecords()]).then((v) => {
          setState((s) => ({ ...s, records: v }));
        });
      }, []);
    }
    

    The [] means that the effect will only run once, on initial mount. If you want listRecords() to be called when other values change, place those values inside of the Array:

    function YourComponent({ id }) {
      const [state, setState] = useState({
        records: []
      });
    
      useEffect(() => {
        Promise.all([listRecords(id)]).then((v) => {
          setState((s) => ({ ...s, records: v }));
        });
      }, [id]);
    }