1

My array isn't getting new entries added to it via the setState getter.

Code:

let calledOut = false;
export default function PayStation () {
    const [records, setRecords] = useState([]);

    // problem snippet
    if (records.length === 0 && !calledOut) {
        calledOut = true;
        fetch('http://localhost:5000/api').then(
            response => response.json()
        ).then(
            data => {
                const payloadRecords = data["records"];
                // returning
                //     [{
                //         "attributes": {
                //             "type": "Contact",
                //             "url": "/services/data/v42.0/sobjects/Contact/0030U00001UPWYKQA5"
                //         },
                //         "Id": "0030U00001UPWYKQA5",
                //         "Name": "Contact25 Sequence Contact Manual Email"
                //     },
                //     {
                //         "attributes": {
                //             "type": "Contact",
                //             "url": "/services/data/v42.0/sobjects/Contact/0030U00001UPWYLQA5"
                //         },
                //         "Id": "0030U00001UPWYLQA5",
                //         "Name": "Contact26 Sequence Contact Manual Email"
                //     }
                // ]
                setRecords((records => [...records, ...payloadRecords]));
                console.log("records size: " + records.length); // why is this still 0?
            }
        );
    }
    // end problem snippet

    return (records.length === 0 ? "loading..." :
        <div style={{
            height: '100vh',
            display: 'flex',
            maxWidth: 600,
            justifyContent: 'space-between',
            alignItems: 'center'
        }} >
            {records}
        </div>
    );
}

I think the requirement for state to change is that you clone the state variable (which I believe I'm doing), as opposed to assigning a reference to it and mutating the reference.

So, why isn't my array getting new entries?

Nicholas Zozaya
  • 129
  • 1
  • 7

2 Answers2

2

The body of the component should be a pure function. Side effects (such as data fetching) should be wrapped in useEffect. The following code should work:

export default function PayStation () {
  const [records, setRecords] = useState([]);

  useEffect(() => {
    const getRecords = () => {
      fetch('http://localhost:5000/api').then(
        response => response.json()
      ).then(
        data => {
          const payloadRecords = data["records"];
          setRecords((records => [...records, ...payloadRecords]));
        }
      );
    }
    
    getRecords()
  }, [])
  
  if (records.length === 0) return "loading..."

  return (
    <div style={{
      height: '100vh',
      display: 'flex',
      maxWidth: 600,
      justifyContent: 'space-between',
      alignItems: 'center'
    }} >
      {records.map((record) => <Record key={record.id} {...record} />)}
    </div>
  );
}
Stafford Rose
  • 769
  • 4
  • 9
  • sorry, I'm still getting 0 records in my array after adding `console.log` below `setRecords()`, I expected the array to have some records in it... – Nicholas Zozaya May 08 '22 at 21:53
  • 2
    It won't show because the state updates happen asynchronously, and the log is run when that variable is still not filled. But it doesn't mean that you don't have a value there. – Konstantin May 08 '22 at 21:54
2

The issue seems to be that useState and setState are both being ran in the same call... And since they're both async the setState isn't setting a value to anything since the state hasn't been created yet. If you removed the calledOut variable it should work fine.

This is generally bad way to do fetching. I recommend doing

  useEffect(() => {
    // fetch stuff here
  }, []);

So this will be invoked after the states are made.

Bas
  • 1,353
  • 3
  • 7
  • 18