useSWR not picking updated state variable

Viewed 839

The token variable is not updating in SWR as I update it via useState in revalidate function.

const [token, setToken] = useState('')

console.log(token) // this updates as setToken is called

const fetcher = (url) => {
    console.log(token) // this remains empty although it re-renders

    return axios.get(
        url,
        {
            headers: {
                'Authorization': `Bearer ${token}`,
            },
        },
    )
    .then(res => res.data)
    .catch(error => {//whatever})
}

const { data: user, error, revalidate } = useSWR('_ENDPOINT_', fetcher)

const login = (email, password) => {
    axios.post('/login', {email, password})
        .then((response) => { 
            setToken(response.data.token)
            revalidate()
        })
}    
2 Answers

I end up using:

'Authorization': `Bearer ${localStorage.getItem('_token').
            replace(/['"]+/g, '')}`,

 'Authorization': `Bearer ${token}` // instead of this using from useState which didn't update in ages

Because it appears localStorage was available after useSWR init; (even on page refreshes)

You have to use useCallback on the fetcher function for it to properly pick up the token state variable change.

const fetcher = useCallback(
    (url) => {
        console.log(token) // Will log the updated `token` value
        return axios
            .get(
                url,
                { headers: { 'Authorization': `Bearer ${token}` } }
            )
            .then(res => res.data)
            .catch(error => {/*whatever*/})
    },
    [token]
)

A better solution, to avoid using useCallback, would be to move the fetcher function outside the component, and pass several arguments to the fetcher in the useSWR call. This has the benefit of using the token as the key for the request (in addition to the URL, making the caching more specific), and only making the request when token is defined.

const fetcher = (url, token) => {
    return axios
        .get(
            url,
            { headers: { 'Authorization': `Bearer ${token}` } }
        )
        .then(res => res.data)
        .catch(error => {/*whatever*/})
}

const SomeComponent = () => {
    const [token, setToken] = useState('')

    const { data: user, error, revalidate } = useSWR(token ? ['_ENDPOINT_', token] : null, fetcher)

    // Rest of the component
}
Related