简体   繁体   English

useEffect 缺少依赖项

[英]useEffect has missing dependency

For the life of me I can't seem to remove the ESlinting warning about my useEffect having a missing dependency fetchProfile().在我的一生中,我似乎无法删除关于我的 useEffect 缺少依赖项 fetchProfile() 的 ESlinting 警告。 When I add fetchProfile to the dependency array, I get an endless loop.当我将 fetchProfile 添加到依赖项数组时,我得到了一个无限循环。 I would really appreciate any suggestions that could help me stifle this warning.我真的很感激任何可以帮助我扼杀这个警告的建议。 The code is as follows:代码如下:

import React, { useEffect, useContext, useReducer } from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import './App.css'
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles/'
import UserContext from './contexts/UserContext'
import jwtDecode from 'jwt-decode'
import axios from 'axios'

// utils
import reducer from './utils/reducer'
import themeFile from './utils/theme'
import AuthRoute from './utils/AuthRoute'
import UnAuthRoute from './utils/UnAuthRoute'

// Components
import NavBar from './components/NavBar'

// Pages
import Home from './pages/Home'
import Login from './pages/Login'
import Profile from './pages/Profile'
import SignUp from './pages/SignUp'
import Admin from './pages/Admin'
import Dashboard from './pages/Dashboard'
import Alumni from './pages/Alumni'

// context
import { ProfileContext } from './contexts/ProfileContext'

const theme = createMuiTheme(themeFile)

// axios.defaults.baseURL = `https://us-central1-jobtracker-4f14f.cloudfunctions.net/api`

const App = () => {
  const initialState = useContext(UserContext)
  const [user, setUser] = useContext(ProfileContext)
  const [state, dispatch] = useReducer(reducer, initialState)

  const fetchProfile = async token => {
    await axios
      .get(`/user`, {
        headers: {
          Authorization: `${token}`
        }
      })
      .then(res => {
        setUser(res.data)
      })
      .catch(err => console.log({ err }))
  }
  // keeps userContext authorized if signed in
  useEffect(
    _ => {
      const token = localStorage.FBIdToken
      if (token && token !== 'Bearer undefined') {
        const decodedToken = jwtDecode(token)
        if (decodedToken.exp * 1000 < Date.now()) {
          localStorage.removeItem('FBIdToken')
          dispatch({ type: 'LOGOUT' })
        } else {
          dispatch({ type: 'LOGIN' })
          state.isAuth && fetchProfile(token)
        }
      } else {
        dispatch({ type: 'LOGOUT' })
        localStorage.removeItem('FBIdToken')
      }
    },
    [state.isAuth]
  )

  return (
    <MuiThemeProvider theme={theme}>
      <UserContext.Provider value={{ state, dispatch }}>
        <div className="App">
          <Router>
            <NavBar isAuth={state.isAuth} />
            <div className="container">
              <Switch>
                <Route exact path="/" component={Home} />
                <UnAuthRoute
                  path="/signup"
                  component={SignUp}
                  isAuth={state.isAuth}
                />
                <UnAuthRoute
                  path="/login"
                  component={Login}
                  isAuth={state.isAuth}
                />
                <AuthRoute
                  path="/profile"
                  component={Profile}
                  isAuth={state.isAuth}
                />
                <AuthRoute
                  path="/dashboard"
                  component={Dashboard}
                  isAuth={state.isAuth}
                />
                <Route path="/admin" component={Admin} isAuth={state.isAuth} />
                <AuthRoute
                  path="/users/:id"
                  component={Alumni}
                  isAuth={state.isAuth}
                />
              </Switch>
            </div>
          </Router>
        </div>
      </UserContext.Provider>
    </MuiThemeProvider>
  )
}

export default App

You can do one of two things:您可以执行以下两项操作之一:

  1. Move fetchProfile out of the component entirely, and use its result instead of having it call setUser directly.fetchProfile完全移出组件,并使用其结果而不是直接调用setUser

  2. Memoize fetchProfile so you only create a new one when something it depends on changes (which is...never, because fetchProfile only depends on setUser , which is stable).记住fetchProfile以便你只在它依赖的东西发生变化时创建一个新的(这是......永远不会,因为fetchProfile只依赖于setUser ,它是稳定的)。 (You'd do this with useMemo or its close cousinuseCallback , probably, though in theory useMemo [and thuse useCallback ] is for performance enhancement, not "semantic guarantee.") (您可能会使用useMemo或其近亲useCallback执行此操作,尽管理论上useMemo [因此useCallback ] 是为了提高性能,而不是“语义保证”。)

For me, #1 is your best bet.对我来说,#1 是你最好的选择。 Outside your component:在您的组件之外:

const fetchProfile = token => {
  return axios
    .get(`/user`, {
      headers: {
        Authorization: `${token}`
      }
    })
}

then然后

useEffect(
  _ => {
    const token = localStorage.FBIdToken
    if (token && token !== 'Bearer undefined') {
      const decodedToken = jwtDecode(token)
      if (decodedToken.exp * 1000 < Date.now()) {
        localStorage.removeItem('FBIdToken')
        dispatch({ type: 'LOGOUT' })
      } else {
        dispatch({ type: 'LOGIN' })
        if (state.isAuth) {                         // ***
            fetchProfile(token)                     // ***
            .then(res => setUser(res.data))         // ***
            .catch(error => console.error(error))   // ***
        }                                           // ***
      }
    } else {
      dispatch({ type: 'LOGOUT' })
      localStorage.removeItem('FBIdToken')
    }
  },
  [state.isAuth]
)

Since the action is asynchronous, you might want to cancel/disregard it if the component re-renders in the meantime (it depends on your use case).由于操作是异步的,如果组件在此期间重新渲染,您可能希望取消/忽略它(这取决于您的用例)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM