简体   繁体   中英

React effect infinite re-renders when fetching data

I want to fetch an array from the backend using a Provider, Context and useEffect:

import React, {useState, useEffect} from 'react'

const UsersContext = React.createContext()

const fetchUsers = async () => {
  const url = 'http://localhost:3000/users'
  const response = await fetch(url)
  console.log('response', await response.json())
  return response
}

export const UsersProvider = ({children}) => {
  // state
  const [users, setUsers] = useState([])

  // query data
  const data = fetchUsers()
  console.log('data', data)

  // component updates
  useEffect(() => {
    if (data) {
      // setUsers(data)
    }
  }, [data])

  return (
    <UsersContext.Provider value={users}>
      {children}
    </UsersContext.Provider>
  )
}

If I set the users once I have the data back from the backend, I get infinite re-render. The issue is, data is always a promise, although I can see the response after the call is being made:

In the fetchUsers method:

console.log('response', await response.json())
{users: Array(1)}
users: Array(1)
0:
created_at: "2019-10-09T17:41:21.818Z"
email: "ash@email.com"
id: 1
name: "ash"
password_digest: "dafasfae"
updated_at: "2019-10-09T17:41:21.818Z"

In the UsersProvider:

console.log('data', data)
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Response
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:3000/users"
__proto__: Response

Your data fetch needs to happen inside the useEffect call. What's happening right now is every time your component renders, you are re-fetching the list of users which causes the data object to change and triggers a re-render. The code below will fetch the data only when the component mounts.

useEffect(() => {
    let canceled = false;

    const fetchData = async () => {
        const users = await fetchUsers();

        if (!canceled && data) {
            setUsers(data);
        }
    };

    fetchUsers();

    return () => { canceled = true; }
}, []);

I would move the fetchUsers function inside the effect and try like this:

import React, { useState, useEffect } from "react";

const UsersContext = React.createContext();

export const UsersProvider = ({ children }) => {
  // state
  const [users, setUsers] = useState([]);

  // component mounts
  useEffect(() => {
    const fetchUsers = async () => {
      const url = "http://localhost:3000/users";
      const response = await fetch(url);
      const usersData = await response.json();
      setUsers(usersData);
    };
    fetchUsers();
  }, []);

  return (
    <UsersContext.Provider value={users}>{children}</UsersContext.Provider>
  );
};

Here is a very good post about fetching data with react hooks:

https://www.robinwieruch.de/react-hooks-fetch-data

return only data

const fetchUsers = async () => {
  const url = 'http://localhost:3000/users'
  const response = await fetch(url)
                         .then( data => {
                             return data
                           })
                         .catch(err=>console.log(err));
  return response;
}

hope this will help you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM