初始情况: 我有一个元素“root”,它在每次调用时检查用户是否经过身份验证。我目前正在尝试新的 React-Router V6 并遇到了所谓的“加载器”。如果我现在通过加载器进行 API 调用,然后想要使用 useState 在元素中设置变量。我收到无限循环错误。
我的 main.jsx(定义路由的地方):
import './index.css'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import authLoader from './loader/AuthLoader'
// -- route elements --
import Root from './routes/root/root'
import Login from './routes/login/login'
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: authLoader
},
{
path: "/login",
element: <Login />
}
], {basename: "/"})
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)
我的AuthLoader.js(API调用的加载器函数):
import { API_HOST } from "../config"
const authLoader = async () => {
return await fetch(`${API_HOST}/auth/me`, {
credentials: 'include'
})
}
export default authLoader
最后是我的 root.jsx
import { useState } from "react"
import { Outlet, useLoaderData } from "react-router-dom"
import { UserContext } from "../../context/UserContext"
const Home = () => {
// states
const [user, setUser] = useState()
const auth = useLoaderData()
if(auth.success) {
/**
* This is not working and throws an infinite loop error
*/
setUser(auth.data.user)
}
return (
<UserContext.Provider value={{user, setUser}}>
<Outlet/>
</UserContext.Provider>
)
}
export default Home
如果我插入
console.log
而不是 setUser(auth.data.user)
,我会得到我期望的响应。但是,当我尝试使用 useState 设置响应中显示的值时,我收到以下错误消息。
有人可以帮助我解决该错误,或者至少解释一下为什么我会收到此错误吗?
非常感谢。
这可能对你有用:
...
// states
const [user, setUser] = useState()
useEffect(() => {
const auth = useLoaderData()
if(auth.success) {
setUser(auth.data.user)
}
},[])
return (
...
当您添加带有空依赖项数组的 useEffect 挂钩时,代码将仅在应用程序第一次渲染后执行一次。
您应该永远不在组件的渲染阶段改变状态。此外,您的组件可以使您的应用程序处于不一致的状态;你可以调用
setUser
,这将使user
和auth.data.user
具有不同的值。此外,您不需要在这里使用状态。您也不需要 useEffect
;如果你有一个 useEffect
只是为了更新状态,那么你使用的钩子是错误的。
我的建议是将你的组件重写为:
const Home = () => {
const auth = useLoaderData()
const userContext = useMemo(() => ({
get user() {
return auth.success ? auth.data.user : null;
}
logout() {
// implement this, should invalidate current user session
}
}), [auth]); // only updates this memo when auth changes
return (
<UserContext.Provider value={userContext}>
<Outlet/>
</UserContext.Provider>
)
}