Reputation: 347
I'm learned that React will re-render after state changed e.g. setState
from useState()
, calling the function or variable from useContext()
variable. But now I'm don't understand that why I get the ESLint
warning call the context function inside the useCallback()
without dependency in the list. If I put the dependency in the list, useCallback()
will be re-rendered and useEffect()
dependency from useCallback()
variable will do again. So how to fix the react-hooks/exhaustive-deps
when calling the function inside the useContext()
variable?
Auth.js
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
import * as AuthAPI from "../API/AuthAPI"
import Loading from "../Page/Loading"
const AuthContext = createContext()
export const AuthProvider = ({children}) => {
const [user,setUser] = useState()
const [loadingInitial,setLoadingInitial] = useState(true)
useEffect(()=>{
AuthAPI.getCurrentUser()
.then((user)=>setUser(user))
.catch((error)=>{console.log(error)})
.finally(()=>setLoadingInitial(false))
},[])
const login = async (email,password) => {
const user = await AuthAPI.login({email,password})
setUser(user)
return user
}
const register = async (firstname,lastname,email,password) => {
const user = await AuthAPI.register({firstname,lastname,email,password})
setUser(user)
return user
}
const logout = async () => {
const response = await AuthAPI.logout()
setUser(undefined)
}
const value = useMemo(()=>({
user,
setUser,
login,
register,
logout
}),[user])
return (
<AuthContext.Provider value={value}>
{loadingInitial ? <Loading/> : children}
</AuthContext.Provider>
)
}
export const useAuth = () => {
return useContext(AuthContext)
}
Logout.js
import { useCallback, useEffect, useState } from "react";
import { Navigate, useLocation, useNavigate } from "react-router-dom";
import { useAuth } from "../Hooks/Auth";
import * as AuthAPI from "../API/AuthAPI"
import Loading from "./Loading";
function Logout() {
const auth = useAuth()
const location = useLocation()
const navigate = useNavigate()
const [isLoggedOut,setIsLoggedOut] = useState(false)
const logout = useCallback(async () => {
console.log("Logging out!")
await AuthAPI.logout()
auth.setUser((prevState)=>(undefined))
setIsLoggedOut(true)
},[auth]) // --> re-rendered because `auth` context in re-rendered when set `user` state.
useEffect(()=>{
logout()
},[logout]) // --> this also to run again from `logout` callback is being re-rendered.
if (!isLoggedOut) {
return <Loading/>
}
return (
<Navigate to="/login" replace/>
)
}
export default Logout
Any help is appreciated.
Upvotes: 1
Views: 2181
Reputation: 202686
There is no need for creating a memoized logout
callback function if logout
isn't used/passed as a callback function. Just apply the logging out logic in the useEffect
hook.
Render the Loading
component and issue the imperative redirect from the resolved Promise chain of the return AuthAPI.logout
Promise.
Example:
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "../Hooks/Auth";
import * as AuthAPI from "../API/AuthAPI"
import Loading from "./Loading";
function Logout() {
const auth = useAuth();
const navigate = useNavigate();
useEffect(() => {
console.log("Logging out!");
AuthAPI.logout()
.then(() => auth.setUser(undefined))
.finally(() => navigate("/login", { replace: true }));
}, []);
return <Loading />;
}
export default Logout;
Upvotes: 1
Reputation: 388
How about destructuring your auth context, since you are only using setUser inside useEffect?
const { setUser } = useAuth()
useEffect(() => {
....
}, [setUser])
Upvotes: 1
Reputation: 319
Can you try to replace your useEffect code into this: useEffect(logout, [])
Upvotes: 0