Home > Software design >  The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change
The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change

Time:05-30

I've been trying to deploy my first React App and i cannot seem to be able to get rid of the following warning which on production stops me from doing a deployment:

Line 60:11: The 'refreshToken' function makes the dependencies of useEffect Hook (at line 142) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'refreshToken' in its own useCallback() Hook react-hooks/exhaustive-deps

Is there an easy way to fix this without breaking the JWT token authentification?

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>
    )
}

CodePudding user response:

Put refreshToken function directly in your useEffect hook or wrap in useCallback.

  • Related