useEffect缺少依赖项

时间:2020-03-25 14:45:28

标签: javascript reactjs react-hooks

在我生命中,我似乎无法删除有关缺少依赖项fetchProfile()的useEffect的ESlinting警告。当我将fetchProfile添加到依赖项数组时,我得到了一个无限循环。我真的很感激任何可以帮助我扼杀此警告的建议。代码如下:

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

1 个答案:

答案 0 :(得分:2)

您可以执行以下两项操作之一:

  1. fetchProfile完全移出组件,并使用其结果而不是直接调用setUser

  2. 记住fetchProfile,以便仅在某些内容依赖于更改时才创建一个新的(这是...从不,因为fetchProfile仅依赖于setUser,即稳定)。 (您可能会使用useMemo或其近亲useCallback进行此操作,尽管理论上useMemo [因此useCallback]是为了提高性能,而不是“语义保证。”)

对我来说,#1是您最好的选择。在组件之外:

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

然后

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

由于操作是异步的,因此如果组件在此期间重新渲染(取决于您的用例),则可能要取消/忽略它。