如何防止 useEffect() 运行 在 运行 在上下文消费者中调用函数后两次并防止 useContext() 重新渲染
How to prevent useEffect() run twice after running a function in context consumer and prevent useContext() to re-render
我了解到 React 会在状态改变后重新渲染,例如setState
来自 useState()
,调用来自 useContext()
变量的函数或变量。但现在我不明白为什么我会收到 ESLint
警告,在列表中没有依赖项的情况下调用 useCallback()
中的上下文函数。如果我将依赖项放入列表中,useCallback()
将被重新渲染,并且来自 useCallback()
变量的 useEffect()
依赖项将再次执行。那么如何在 useContext()
变量中调用函数时修复 react-hooks/exhaustive-deps
呢?
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 bacause `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
感谢任何帮助。
您能否尝试将您的 useEffect 代码替换为:
使用效果(注销,[])
既然你只在 useEffect 中使用 setUser,那么解构你的 auth 上下文怎么样?
const { setUser } = useAuth()
useEffect(() => {
....
}, [setUser])
如果 logout
不是 used/passed 回调函数,则无需创建记忆 logout
回调函数。只需在 useEffect
挂钩中应用注销逻辑。
呈现 Loading
组件并从 return AuthAPI.logout
Promise 的已解析 Promise 链发出命令式重定向。
示例:
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;
我了解到 React 会在状态改变后重新渲染,例如setState
来自 useState()
,调用来自 useContext()
变量的函数或变量。但现在我不明白为什么我会收到 ESLint
警告,在列表中没有依赖项的情况下调用 useCallback()
中的上下文函数。如果我将依赖项放入列表中,useCallback()
将被重新渲染,并且来自 useCallback()
变量的 useEffect()
依赖项将再次执行。那么如何在 useContext()
变量中调用函数时修复 react-hooks/exhaustive-deps
呢?
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 bacause `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
感谢任何帮助。
您能否尝试将您的 useEffect 代码替换为: 使用效果(注销,[])
既然你只在 useEffect 中使用 setUser,那么解构你的 auth 上下文怎么样?
const { setUser } = useAuth()
useEffect(() => {
....
}, [setUser])
如果 logout
不是 used/passed 回调函数,则无需创建记忆 logout
回调函数。只需在 useEffect
挂钩中应用注销逻辑。
呈现 Loading
组件并从 return AuthAPI.logout
Promise 的已解析 Promise 链发出命令式重定向。
示例:
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;