Search code examples
javascriptreactjsreact-hooksuse-effect

How can I escape React useEffect infinite loop?


I wrote a code that receives the api through React Hook and calls the api again in the child component by passing the id when clicking. However, there seems to be a problem in settingState with arrow function in useEffect or onClick of child component.

I would appreciate it if you could give me an answer on how to fix it.

Users.js

import axios from 'axios';
import React, { useState, useEffect } from 'react';
import UserInfo from './UserInfo';


function Users() {
    const [users, setUsers] = useState(null)
    const [loding, setLoding] = useState(false)
    const [error, setError] = useState(false)


    const [userId, setUserId] = useState(null)

    const fetchUsers = async () => {
        try {
            setUsers(null)
            setError(null);
            setLoding(true)

            const respnse = await axios.get('https://jsonplaceholder.typicode.com/users')

            setUsers(respnse)
        } catch (e) {
            setError(e)
        }
        setLoding(false)
    };

    useEffect(() => {
        fetchUsers();
    }, []);

    if (loding) return <div>loading...</div>
    if (error) return <div>error....</div>
    if (!users) return null;

    return (
        <>
            <ul>
                {
                    users.data.map(user =>
                        <li key={user.id} onClick={() => setUserId(user.id)} >
                            {user.username} ({user.name})
                        </li>
                    )
                }
            </ul>
            <button onClick={fetchUsers}>
                reload
            </button>
            {userId && <UserInfo id={userId} />}
        </>
    );
}

export default Users;

UserInfo.js

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

function UserInfo({ id }) {

    const [userInfo, setUserInfo] = useState(null)

    async function getUsersAPI() {
        try {
            const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)
            setUserInfo(response)
        } catch (e) {

        }
    }

    useEffect(() => {
        getUsersAPI()
    }, [userInfo])


    if (!userInfo) {
        return null;
    }

    const { data } = userInfo

    return (
        <>
            <h2>{data.username}</h2>
            <p>
                <b>email: </b>{data.email}
            </p>
        </>
    );
}

export default UserInfo;

Solution

  • It seems like you want id to be a useEffect dependency instead of userInfo.

    Otherwise every time userInfo changes the effect will run, it will call getUsersAPI which in turns sets the value of userInfo when axios resolves (thus causing an infinite loop).

        import React, { useEffect, useState } from 'react';
        import axios from 'axios';
    
        function UserInfo({ id }) {
            const [userInfo, setUserInfo] = useState(null)
            async function getUsersAPI() {
                try {
                    const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)
                    setUserInfo(response)
                } catch (e) {}
            }
    
            useEffect(() => {
                getUsersAPI()
            }, [id])
    
            if (!userInfo) {
                return null;
            }
    
            const { data } = userInfo
    
            return (
                <>
                    <h2>{data.username}</h2>
                    <p>
                        <b>email: </b>{data.email}
                    </p>
                </>
            );
        }
    
        export default UserInfo;