'refreshToken' 函数使 useEffect Hook 的依赖项(第 142 行)在每次渲染时都发生变化

The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change on every render

我一直在尝试部署我的第一个 React 应用程序,但我似乎无法摆脱以下警告,该警告在生产中阻止我进行部署:

第 60:11 行:'refreshToken' 函数使 useEffect Hook 的依赖项(第 142 行)在每次渲染时都会发生变化。将它移到 useEffect 回调中。或者,将 'refreshToken' 的定义包装在它自己的 useCallback() Hook react-hooks/exhaustive-deps

有没有一种简单的方法可以在不破坏 JWT 令牌身份验证的情况下解决这个问题?

AuthContext.js

import React, { useEffect, useState, useCallback } from 'react'
import { API } from "../api"
import axios from "axios"
import { isAfter, isEqual, parseISO, sub } from 'date-fns'

export const AuthContext = React.createContext(null)

export function AuthContextProvider({ children }) {

    const [accessTokenExpiration, setAccessTokenExpiraton] = useState(undefined);

    const getUser = () => {
        return JSON.parse(localStorage.getItem('user'))
    }

    const isLoggedIn = () => {
        return localStorage.getItem('user') !== null
    }

    const [user, setUser] = useState(() => {
        return isLoggedIn() ? getUser() : null;
    })

    const [shouldGoToLogin, setShouldGoToLogin] = useState(() => {
        if (!user || !user.access_token || !user.refresh_token) {
            return true;
        }

        return false;
    })

    const logout = async () => {
        if (!user) {
            return;
        }

        const { access_token } = user;
        localStorage.removeItem('user')
        setUser(null);

        return axios.post(API.auth.logout, {
            headers: {
                "Authorization": `Bearer ${access_token}`,
                "Content-Type": "application/json"
            },
            withCredentials: true
        });
    }
    
    const login = async (values) => {
        console.log(values);
        const correctedValues = { ...values, username: values.email };
        return axios.post(API.auth.login, correctedValues)
            .then(res => {
                const data = res.data;
                processApiData(data);
            })
    }
    const processApiData = useCallback((resp) => {
        let newUser = { ...user, ...resp };
        delete(newUser.user); // Delete the user sub-object since we merged that directly into the top-level object
        saveUser(newUser); // Save the user

        const { access_token_expiration } = newUser;

        if (access_token_expiration) {
            console.log("have expiration", access_token_expiration);
            const nextExpiration = parseISO(access_token_expiration); // Convert from ISO 8601 to a Date Object
            const earlyRefreshTime = sub(nextExpiration, { minutes: 55 }); // Do an hourish early
            setAccessTokenExpiraton(earlyRefreshTime); // Set the upcoming expiraton
        }
    }, [user])
    const refreshToken = useCallback(async () => {
        const user = getUser();

        const redirectToLogout = () => {
            localStorage.clear(); // Clear our localStorage
            setShouldGoToLogin(true);
        };

        if (!user) { // No user
            redirectToLogout();
        }

        console.log(API.auth.refreshToken);
        const resp = await fetch(API.auth.refreshToken, {
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({'refresh': user?.refresh_token}),
            method: "POST",
            withCredentials: true
        })

        console.log("status", resp.status);
        if (resp.status === 200) {
            const data = await resp.json(); // Convert to JSON
            console.log("refresh token data", data);
            processApiData(data);
        } else {
            redirectToLogout();
        }
    }, [processApiData])

    const resetPassword = async (values) => {
        return axios.post(API.auth.passwordReset, values);
    }



    const saveUser = async (newUser) => {
        localStorage.setItem('user', JSON.stringify(newUser))
        setUser(newUser)
    }

    const signup = async (values) => {
        return axios.post(API.auth.signup, values);
    }

    useEffect(() => {
        
        if (!user) {
            return;
        }

        const interval = setInterval(()=> {
            if(!user){
                return false;
            }

            if (accessTokenExpiration) {
                const now = new Date(); // Get the current time
                console.log(now);
                console.log(accessTokenExpiration);
                if (isAfter(now, accessTokenExpiration) || isEqual(now, accessTokenExpiration)) { // If we are late to the party or the stars have aligned
                    refreshToken(); // Refresh the token
                }
            } else { // We do not have an access token expiration yet
                refreshToken(); // Refresh the token immediately so we get a time
            }
        }, 1000 * 15)
        return ()=> clearInterval(interval)
    }, [accessTokenExpiration, refreshToken, user])

    return (
        <AuthContext.Provider value={{
            getUser,
            isLoggedIn,
            logout,
            login,
            resetPassword,
            signup,
            user,
            shouldGoToLogin
        }}>
            {children}
        </AuthContext.Provider>
    )
}

将 refreshToken 函数直接放在您的 useEffect 挂钩中或包装在 useCallback 中。