4

I'm working with react-table library for filtering and pagination. I am requesting data from a custom API using Node and rendering it using useTable hook from react-table. The documentation for react-table says the table data should be memoized, so I use the useMemo hook. The code is as shown:

  const data = React.useMemo(() => {
    axios.get("/custom/api/endpoint")
      .then((res) => {
        return res.data;   //Returns an array of objects
      })
      .catch((err) => {
        console.log("err");
      });
  }, []);

However, I am getting the following error:

Uncaught TypeError: Cannot read property 'forEach' of undefined
    at accessRowsForColumn (useTable.js:591)
    at useTable.js:195
    .
    .

Can anyone help me figure this? Thanks in advance!

yksolanki9
  • 429
  • 2
  • 7
  • 14

4 Answers4

3

You are memoizing the promise that axios creates. Instead make the call with useEffect(), and set the state. Unless you'll set the state again, the data would not change:

const [data, setData] = useState([]); // use an empty array as initial value

useEffect(() => {
  axios.get("/custom/api/endpoint")
    .then((res) => {
      setData(res.data); // set the state
    })
    .catch((err) => {
      console.log("err");
    });
}, []);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • useEffect is not memoization. useEffect is the act of comparing equality & recalculating a page although not caching. useMemo is for caching. – Jackie Santana Aug 01 '22 at 15:17
  • 1
    @JackieSantana "useEffect is not memoization" - indeed it's not, and it's also not the right tool for this problem. That's why the solution is to use `useEffect` for this case. Now days, you'll probably want to use one of the suspense based solutions (I like SWR). – Ori Drori Aug 01 '22 at 16:40
1

You have defined a promise and not returning it, so the code is not waiting for the promise to resolve. Try with async await syntax, which is easier to visualize the flow.

  const data = React.useMemo(async () => {
    
      try {
        const res = await axios.get("/custom/api/endpoint") 
        return res.data
      } catch(err) {
        throw err
      }
 
  }, []);
  • 4
    It's not a good idea to do an async call within `useMemo`. From React documentation: "Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo." – Evrim Persembe May 22 '21 at 20:48
0

You're using this arrow function inside the useMemo call.

() => {
    axios.get("/custom/api/endpoint")
      .then((res) => {
        return res.data;   //Returns an array of objects
      })
      .catch((err) => {
        console.log("err");
      });
  }

And it returns undefined. It's because axios.get returns a Promise and you're not assigning it to anything and your arrow function implicitly returns undefined as it doesn't have an explicit return statement.

Try this:

const [data, setData] = useState([]);

useEffect(() => {
  axios.get("/custom/api/endpoint")
    .then((res) => {
      setData(res.data);
    })
    .catch((err) => {
      console.log("err");
    });
}, []);

Here useEffect is called after the initial render of the component, which calls axios.get. It is an async function so the useEffect call immediately returns after calling it. When the axios.get async call succeeds it calls the arrow function within the then call which assigns the data from the response to the data state variable.

Evrim Persembe
  • 497
  • 5
  • 11
0

These solutions not succesful need for me, because I structured my pattern for project. My data request with redux dispatch (so redux-thunk) actually I used redux-toolkit for fast development.

I solved like this;

function Table ({ columns, data }) {
    // ...
    return /* standart react-table basic component */
}

function MyTable(props) {
  const columns = useMemo(() => props.columns, [props.columns]);
  const data = useMemo(() => props.data, [props.data]);

  return <Table columns={columns} data={data} />;
}

const PageListTable = () => {
    // ...
    const columns = [...]; // static variable

    useEffect(
      async () => {
        const payload = {};

        // some codes

        dispatch(getAllData(payload));
      },
      [activeId, isAuth] // listen from with useSelector
    );

    // some codes..

    return (
        {/* ..... */}

        <HOC.Async status={loading}>
            <MyTable
                columns={columns}
                data={getAllDataState.data}
            />
        </HOC.Async>

        {/* ..... */}
    );
}

My package.json specs like this;

"react": "^17.0.2",
"react-redux": "^7.2.4",
"react-table": "^7.7.0",
"@reduxjs/toolkit": "^1.6.1",
Fatih Mert Doğancan
  • 1,016
  • 14
  • 21