React 路由器 <Redirect> 未根据状态重定向
React router <Redirect> is not redirecting on state
当 user
不是 null
时,应用应该重定向到 /dashboard
。用户在第一个渲染中是 null
,但 useEffect()
应该从本地存储中获取和存储用户数据。然后,三元运算符应该触发 <Redirect to='/dashboard' />
并重定向到仪表板页面。但是,它并没有这样做。每当我刷新时,即使 localStorage 中有数据,它也会一直在 /login
显示登录页面。
我做错了什么或者我该如何解决这个问题?
const App = () => {
const [user, setUser] = useState(null)
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
noteService.setToken(user.token)
}
}, [])
return (
<Router>
<Switch>
<Route exact path='/'>
{user
? <Redirect to='/dashboard' />
: <Redirect to='/login' />
}
</Route>
<Route exact path='/login'>
<LogIn />
</Route>
<Route exact path='/dashboard'>
<Dashboard />
</Route>
</Switch>
</Router>
)
问题
问题是在初始渲染时 user
状态是 null
并且重定向到“/login”。当您重新加载页面时,如果路径仍然是“/login”,它不会完全匹配“/”并且不会发生重定向,无论user
个州。
解决方案
从 localStorage 初始化状态而不是等待渲染周期。这至少允许您在第一次渲染而不是第二次(或更高版本)重新渲染时检查 user
状态。重新排序 Switch
组件中的路由,以指定它们从 more 具体到 less 具体。重定向应该放在最后,以便其他路径有机会首先匹配和呈现。
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<Route path='/dashboard'>
<Dashboard />
</Route>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};
这并没有完全解决用户已经在子路由上的页面重新加载问题。为此,您 应该 实现一个 ProtectedRoute
组件并将 user
状态作为道具传递。如果有一个 user
对象,它会呈现一个常规的 Route
组件并传递道具,否则它会呈现一个 Redirect
并将用户跳转到登录路由。
const ProtectedRoute = ({ user, ...props }) => user ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<ProtectedRoute path='/dashboard' user={user} >
<Dashboard />
</ProtectedRoute>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};
如果您使用的是版本 6,请阅读以下内容:reactrouter.com/docs/en/v6/upgrading/v5
特别是它告诉您删除直接位于 <Switch>
.
内的所有 <Redirect>
元素的部分
当 user
不是 null
时,应用应该重定向到 /dashboard
。用户在第一个渲染中是 null
,但 useEffect()
应该从本地存储中获取和存储用户数据。然后,三元运算符应该触发 <Redirect to='/dashboard' />
并重定向到仪表板页面。但是,它并没有这样做。每当我刷新时,即使 localStorage 中有数据,它也会一直在 /login
显示登录页面。
我做错了什么或者我该如何解决这个问题?
const App = () => {
const [user, setUser] = useState(null)
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
noteService.setToken(user.token)
}
}, [])
return (
<Router>
<Switch>
<Route exact path='/'>
{user
? <Redirect to='/dashboard' />
: <Redirect to='/login' />
}
</Route>
<Route exact path='/login'>
<LogIn />
</Route>
<Route exact path='/dashboard'>
<Dashboard />
</Route>
</Switch>
</Router>
)
问题
问题是在初始渲染时 user
状态是 null
并且重定向到“/login”。当您重新加载页面时,如果路径仍然是“/login”,它不会完全匹配“/”并且不会发生重定向,无论user
个州。
解决方案
从 localStorage 初始化状态而不是等待渲染周期。这至少允许您在第一次渲染而不是第二次(或更高版本)重新渲染时检查 user
状态。重新排序 Switch
组件中的路由,以指定它们从 more 具体到 less 具体。重定向应该放在最后,以便其他路径有机会首先匹配和呈现。
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<Route path='/dashboard'>
<Dashboard />
</Route>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};
这并没有完全解决用户已经在子路由上的页面重新加载问题。为此,您 应该 实现一个 ProtectedRoute
组件并将 user
状态作为道具传递。如果有一个 user
对象,它会呈现一个常规的 Route
组件并传递道具,否则它会呈现一个 Redirect
并将用户跳转到登录路由。
const ProtectedRoute = ({ user, ...props }) => user ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));
const App = () => {
const [user] = useState(initializeState());
useEffect(() => {
noteService.setToken(user.token);
}, [user]);
return (
<Router>
<Switch>
<Route path='/login'>
<LogIn />
</Route>
<ProtectedRoute path='/dashboard' user={user} >
<Dashboard />
</ProtectedRoute>
<Redirect to={user ? '/dashboard' : '/login'} />
</Switch>
</Router>
);
};
如果您使用的是版本 6,请阅读以下内容:reactrouter.com/docs/en/v6/upgrading/v5
特别是它告诉您删除直接位于 <Switch>
.
<Redirect>
元素的部分