Home > Net >  useContext returns values multiple times when called only 1 time
useContext returns values multiple times when called only 1 time

Time:03-11

I'd like to get the data from my API and store it in a context in order to use that data on multiple component. There are multiple endpoints from this API, and I need the user id to reach them. To do that, I get the id from useParams and change the id with the hook setID passed by the context.

The problem is : when I console.log the data after I change the ID, the console display 4 differents data, one by step in the useEffect :

  • First with the initial state of context (I understand that one)
  • Second with the ID changed
  • Third with the error changed
  • And the last one with the data changed

But I only set the id in the useEffect callback, so I don't understand why it fires 4 times when value is changed 1 time.

I don't know if I'm doing it in the right way (if using context is a bad thing there), or just I misunderstand useEffect and useContext.

// Component receiving the data
function APIChecker(){
    const {id} = useParams();
    const data = useContext(DataContext);
    data.setID(id);
    
    console.log(data);

    return(
        <>
        </>
    );
}

export default APIChecker;
// Context
export const DataContext = React.createContext();

export const DataProvider = ({children}) => {
    const [id, setID] = useState(null);

    const initialData = {
        globalInfo: [],
        activity: [],
        averageSession: [],
        performance: []
    }

    const [data, setData] = useState(initialData);
    const [error, setError] = useState(404);

    useEffect(() => {
        const url = "http://localhost:3000/user/";
        const fetchData = async () =>{
            Promise.all([
                fetch(url   id),
                fetch(url   id   "/activity"),
                fetch(url   id   "/average-sessions"),
                fetch(url   id   "/performance")
            ]).then(([responseGlobal, responseActivity, responseSessions, responsePerformance]) => Promise.all([responseGlobal.json(), responseActivity.json(), responseSessions.json(), responsePerformance.json(), responseGlobal.status
            ])).then(([responseGlobal, responseActivity, responseSessions, responsePerformance, statusError]) => {
                setError(statusError);
                setData({
                    globalInfo: responseGlobal,
                    activity: responseActivity,
                    averageSessions: responseSessions,
                    performance: responsePerformance
                });
            }).catch(error => {
                throw new Error(error);
            });
        }
        if(id != null){
            fetchData();
        }

    }, [id])

    return(
        <DataContext.Provider value={{id, data, setID, error}}>
            {children}
        </DataContext.Provider>
    )
}

CodePudding user response:

Everytime your child component APIChecker invokes setID, that is going to trigger the useEffect in the parent since in the dependancy array you have passed id. This in turn is calling the APIs multiple times.

useEffect(()=>{
    /*your implementation*/
},[id]);

if you want this invoked just once, just pass empty array as dependancy, like this:

useEffect(()=>{
    /*your implementation*/
},[]);
  • Related