Nils
Nils

Reputation: 2205

How do I setState properly in a functional React component?

I have implemented React State Hook in a functional React component like this:

const [tableData, setTableData] = useState([]);

In my component I perform an Asynchronous API request and fill a table with the result:

const loadArtikel = () =>
    fetch("https://my-api.com/api/v1/xyz")
        .then(res => (res.ok ? res : Promise.reject(res)))
        .then(res => res.json())


return (
    <Async promiseFn={loadArtikel}>
        {({ data, err, isLoading }) => {
            if (isLoading) return (<div>Loading...</div>)
            if (err) return `Something went wrong: ${err.message}`

            if (data) {
                var tempData = [];
                Object.keys(data.artikel).map(function (keyName, keyIndex) {
                    tempData.push(data.artikel[keyName]);
                });
                setTableData(tempData); // this line is causing problems
                return (
                    <MaterialTable
                        data={tableData}
                    />
                )
            }
        }}
    </Async>
);

When I use setTableData(), this leads to the fetch request being repeated over and over. I can't tell why.

Upvotes: 0

Views: 44

Answers (1)

wentjun
wentjun

Reputation: 42516

This is actually causing an infinite loop, as the component will re-render each time its props/state changes. In this case, you are directly calling setTableData within the return of your component, which updates the state. This will in turn cause the component to re-render again, thus causing the request to be called over and over again.

If this method is meant to be called only once, you should be using it within the useEffect hook, with an empty array as the dependency array:

useEffect(() => {  
  fetch('https://my-api.com/api/v1/xyz')
    .then(response => response.json())
    .then(result => {
      // process data
      const tempData = ......;
      setTableData(tempData);
    });     
}, []);


return (
  <>
  {
    tableData.length 
      ? (
        <MaterialTable
          data={tableData}
        />
      : <div>Loading...</div>
  } 
  </>
);

Upvotes: 1

Related Questions