我有一个相当基本的用例:我想在应用程序加载时从服务器获取用户信息,然后使用钩子获取不同组件中的信息。
出于某种原因,我遇到了一个无限循环并得到了Error: Maximum update depth exceeded.
getMe
被递归调用,直到应用崩溃。 这是正确的钩子行为吗?
这是钩子的相关部分:
export default function useUser () {
const [user, setUser] = useState(null)
const [authenticating, setAuthenticating] = useState(true)
// ...
const getMe = (jwt) => {
console.log('set user')
axios.get(baseURL + endpoints.currentUser, { headers: {
'X-Access-Token': jwt,
'Content-Type': 'application/json'
}}).then(response => {
setUser({
name: response.data.name,
img: response.data.avatar_url
})
})
}
useEffect(() => {
getMe(jwt)
}, [])
return { user, authenticating }
}
这是第一次通话
function App () {
const { user, authenticating } = useUser()
const c = useStyles()
return (
authenticating ? (
<div className={c.wrapper}>
<Loader size={60}/>
</div>
) : (
<div className={c.wrapper}>
<div className={c.sidebar}>
<img className={c.lamp} src={ user ? user.img : lamp } />
我还在路由器组件中调用需要用户
const Routes = () => {
const { user } = useUser()
return (
<Router history={history}>
<Switch>
// ...
<Route
path={pages.login}
render={routeProps => (
user ?
<Redirect to={pages.main}/> :
<Login {...routeProps}/>
)}
/>
你不应该每次调用钩子时都请求服务器,因为它几乎是不必要的。你可以为此使用 Redux 或上下文(对于这种范式,redux 会更好(。 但是,如果您坚持使用此方法,则似乎必须将getMe
函数包装在useCallback
钩子中,因为每次函数运行时都必须重新呈现。
阅读有关useCallback
钩的更多信息:
https://reactjs.org/docs/hooks-reference.html#usecallback
您现在通过自定义钩子中的useEffect
发出请求 - 为什么不让组件以编程方式执行此操作?
将getMe
更改为useCallback
函数并将其导出:
export default function useUser () {
const [user, setUser] = useState(null)
const [authenticating, setAuthenticating] = useState(true)
// ...
const getMe = useCallback((jwt) => {
console.log('set user')
axios.get(baseURL + endpoints.currentUser, { headers: {
'X-Access-Token': jwt,
'Content-Type': 'application/json'
}}).then(response => {
setUser({
name: response.data.name,
img: response.data.avatar_url
})
})
}, [])
return { user, authenticating, doFetch: getMe }
}
..并在组件中使用该函数(导入doFetch
并在挂载时调用它(,例如:
function App () {
const { user, authenticating, doFetch } = useUser()
const c = useStyles()
useEffect(() => doFetch(), [doFetch])
return (
authenticating ? (
<div className={c.wrapper}>
<Loader size={60}/>
</div>
) : (
<div className={c.wrapper}>
<div className={c.sidebar}>
<img className={c.lamp} src={ user ? user.img : lamp } />
您现在避免了无限循环,您的组件控制了请求逻辑而不是可重用的钩子。