Почему не axios получает токен только после перезагрузки страницы?

Когда прохожу авторизацию jwt токен записывается в localstorage. Но в запросах токен не летит, после перезагрузки страницы всё нормально

Хук useAuth для авторизации:


    import { useState, useCallback, useEffect } from 'react'
    const storageName = 'test'

    export const useAuth = () => {
      const [token, setToken] = useState(null)
      const [ready, setReady] = useState(false)
      const [userId, setUserId] = useState(null)

      const login = useCallback((jwtToken, id) => {
        setToken(jwtToken)
        setUserId(id)

        window.localStorage.setItem(storageName, JSON.stringify({
          userId: id, token: jwtToken
        }))
      }, [])


      const logout = useCallback(() => {
        setToken(null)
        setUserId(null)
        window.localStorage.removeItem(storageName)

      }, [])

      useEffect(() => {
        const data = JSON.parse(window.localStorage.getItem(storageName))

        if (data && data.token) {
          login(data.token, data.userId)
        }
        setReady(true)
      }, [login])


      return { login, logout, token, userId: userId, ready }
    }

    import axios from 'axios'
    const development = process.env.NODE_ENV !== 'production'

    const getToken = () => {
      let token = null
      const localStorage = window.localStorage.getItem('test')
      if (localStorage) token = JSON.parse(localStorage).token
      return token
    }

    export default axios.create({
      baseURL: development ? 'http://localhost:5005' : 'https://test.com',
      headers: { authorization: `Bearer ${getToken()}` }
    })

Функция подтверждение авторизации:

      const onSubmitAuth = useCallback(async values => {
        await axios.post(`/api/auth`, values)
          .then(res => {
            login(res.data.token, res.data.id)
            history.push('/hero/city')
          })
          .catch(err => {
            setError(err)
          })
      }, [])

Ответы (2 шт):

Автор решения: Nikita Umnov
import axios from 'axios'
const development = process.env.NODE_ENV !== 'production'

const getToken = () => {
  let token = null
  const localStorage = window.localStorage.getItem('test')
  if (localStorage) token = JSON.parse(localStorage).token
  return token
}

const instance = axios.create({
  baseURL: development ? 'http://localhost:5005' : 'https://test.com'
})

instance.interceptors.request.use((config) => {
  config.headers.Authorization = `Bearer ${getToken()}`
  return config
})

export default instance
→ Ссылка
Автор решения: yar85

Можно просто обновлять значение заголовка в дефолтных настройках экземпляра axios (созданного при экспорте модуля).

Либо в login, если не смущает импорт экземпляра axios в том же модуле:

const login = useCallback((jwtToken, id) => {
  setToken(jwtToken)
  setUserId(id)

  window.localStorage.setItem(storageName, JSON.stringify({
    userId: id, token: jwtToken
  }))
  axios.defaults.headers.authorization = `Bearer ${jwtToken}`
}, [])

, либо в onSubmitAuth (здесь это более логично, по-моему):

const onSubmitAuth = useCallback(async values => {
  await axios.post(`/api/auth`, values)
    .then(res => {
      login(res.data.token, res.data.id)
      axios.defaults.headers.authorization = `Bearer ${res.data.token}`
      history.push('/hero/city')
    })
    .catch(err => {
      setError(err)
    })
}, [])

Разница с вариантом принятого ответа - в том, что эти два варианта не потребуют никакого усложнения перехватчиком запросов (который вызывается при каждом запросе, и при каждом запросе он будут через функцию getToken дергать localStorage, бесполезно перечитывая и репарся одно и то же значение).
Мы добавляем токен в общую конфигурацию при его получении, и все.

→ Ссылка