我正在使用 Meteor.js 和 React 构建一个 Web 应用程序。该应用程序有两种类型的用户:“用户”和“雇主”,每种用户都有自己的路线(如不同的仪表板和其他页面)。所以我有一个
UserRouter
、一个 EmployerRouter
和一个 UnprotectedRouter
(当用户尚未登录时)。我想根据登录的用户返回不同的路由器(我有一个“类型”字段让我知道他们是什么用户类型)。问题是当用户登录时路由器不会更新。基本上我需要不同的路由器,不仅取决于用户是否登录,还取决于用户的类型。
我尝试使用
useState
,但它不会更新路由器。然后我想,因为浏览器在用户注册后重新路由,也许它会重新运行 App.tsx
,这会将路由器更新为正确的路由器,但这也不起作用。现在,当我启动应用程序时,它会呈现 UnprotectedRouter
,因为我尚未登录。然后,当我转到注册表单并创建一个新用户(自动登录)时,它仍然显示 UnprotectedRouter
而不是 UserRouter
。
这就是我的代码目前的样子:
export const UserRouter = () => {
return (
<Routes>
<Route path={RoutePaths.USER_HOME} element={<UserHome />} />
<Route path={RoutePaths.USER_PROFILE} element={<UserProfile />} />
<Route path={RoutePaths.EMPLOYER_HOME} element={<EmployerHome />} />
<Route path={RoutePaths.USER_SIGNUP} element={<UserSignUp />} />
<Route path={RoutePaths.EMPLOYER_SIGNUP} element={<EmployerSignUp />} />
<Route path={RoutePaths.USER_SIGNIN} element={<UserSignIn />} />
<Route path="/position/:postingId" element={<UserPosting />}/>
</Routes>
);
};
export const EmployerRouter = () => {
return (
<Routes>
<Route path={RoutePaths.USER_HOME} element={<EmployerDashboard />} />
</Routes>
);
};
export const UnprotectedRouter = () => {
return (
<Routes>
<Route path={RoutePaths.WELCOME_PAGE} element={<WelcomePage />} />
<Route path={RoutePaths.USER_SIGNUP} element={<UserSignUp />} />
<Route path={RoutePaths.EMPLOYER_SIGNUP} element={<EmployerSignUp />} />
</Routes>
);
};
const selectRouter = (userType: String) => {
switch (userType) {
case "user":
return <UserRouter />
case "employer":
return <EmployerRouter />
default:
return <UnprotectedRouter />
}
}
const RouterChoice = () => {
const isLoadingProfiles = useSubscribe("allUserProfiles");
const loggedInUsername = Meteor.user()?.username;
const userType = UserCollection.findOne({ username: loggedInUsername })?.type;
return selectRouter(userType)
}
export const App = () => {
const router = RouterChoice();
return (
<BrowserRouter>
<div>
{router}
</div>
</BrowserRouter>
)
};
这就是我的用户注册方法:
const handleSignUp = (e: { preventDefault: () => void }) => {
e.preventDefault();
console.log("password is: " + password);
console.log("username is: " + username);
console.log("email is: " + email);
Accounts.createUser(
{
username: username,
password: password,
email: email,
},
(error) => {
if (error) {
console.log(error);
return;
} else {
console.log("success!");
}
}
);
Meteor.call("user-profile.createNewProfile", {
username: username,
name: name,
type: "user",
});
setName("");
setUsername("");
setEmail("");
setPassword("");
console.log("submitted!");
navigate(RoutePaths.USER_HOME);
};
我渲染的内容在 App.tsx 上,你需要包装
const router = RouterChoice();
由
useEffect(() => {}, [])
像这样:
useEffect(() => {
const router = RouterChoice();
}, [Meteor.user()?.username])
对于我使用的依赖项<
问题似乎是你错误地设置了
RouterChoice
函数名称,它“欺骗”React,或者更确切地说“钩子规则”,让其认为 RouterChoice
是一个 React 组件而不是常规回调函数。 RouterChoice
调用 useSubscribe
React 钩子,但因为 App
没有被触发重新渲染,所以它不会再次调用 useSubscribe
,因为 RouterChoice
永远不会再次作为 React 组件“被调用”。
将
RouterChoice
重命名为 useRouterChoice
之类的名称,以便将其作为自定义 React 钩子有效调用。
const selectRouter = (userType: String) => {
switch (userType) {
case "user":
return <UserRouter />;
case "employer":
return <EmployerRouter />;
default:
return <UnprotectedRouter />;
}
};
const useRouterChoice = () => {
const isLoadingProfiles = useSubscribe("allUserProfiles");
const loggedInUsername = Meteor.user()?.username;
const userType = UserCollection.findOne({ username: loggedInUsername })?.type;
return selectRouter(userType);
};
export const App = () => {
const router = useRouterChoice();
return (
<BrowserRouter>
<div>
{router}
</div>
</BrowserRouter>
);
};
您现在可能遇到的问题是,当身份验证状态/条件发生变化时,UI 和其他路由可能尚未安装和导航。为此,最好无条件渲染所有路线并保护/守卫对路线的访问。
这是使用布局路由的常见路由保护实现。
const ProtectedRoute = ({ type }: { type: "user" | "employer"}) => {
const isLoadingProfiles = useSubscribe("allUserProfiles");
const loggedInUsername = Meteor.user()?.username;
const userType = UserCollection.findOne({ username: loggedInUsername })?.type;
if (isLoadingProfiles) {
return null; // or loading indicator/spinner/etc
}
return userType === type
? <Outlet />
: <Navigate to="/login" replace />;
};
export const App = () => {
const router = useRouterChoice();
return (
<BrowserRouter>
<div>
<Routes>
{/* unprotected routes */}
<Route element={<ProtectedRoute type="user" />}>
{/* protected user routes */}
</Route>
<Route element={<ProtectedRoute type="employer" />}>
{/* protected employer routes */}
</Route>
</Routes>
</div>
</BrowserRouter>
);
};
如果您希望经过身份验证的用户无法访问“未受保护”的路由,则可以使用另一个布局路由组件来保护它们,该组件应用
ProtectedRoute
组件的逆逻辑。
const AnonymousRoute = () => {
const isLoadingProfiles = useSubscribe("allUserProfiles");
const loggedInUsername = Meteor.user()?.username;
const userType = UserCollection.findOne({ username: loggedInUsername })?.type;
if (isLoadingProfiles) {
return null; // or loading indicator/spinner/etc
}
return !!userType
? <Navigate to={/* any safe authenticated/protected route path */} replace />
: <Outlet />;
};
export const App = () => {
const router = useRouterChoice();
return (
<BrowserRouter>
<div>
<Routes>
<Route element={<AnonymousRoute />}>
{/* unprotected routes */}
</Route>
<Route element={<ProtectedRoute type="user" />}>
{/* protected user routes */}
</Route>
<Route element={<ProtectedRoute type="employer" />}>
{/* protected employer routes */}
</Route>
</Routes>
</div>
</BrowserRouter>
);
};