我正在尝试建立一个Router
来使用自定义PrivateRoute
组件,这些组件与官方react-router
文档中给出的example等效。问题是我还想使用全部捕获的404路由,所以我认为我也需要使用Switch
组件,如文档中的this example所示。
这里存在冲突,因为Switch
的{{3}}声明Switch
组件的子代必须是Route
组件,而在docs中,小组指出,不支持在Switch
内使用非路由组件,即使它确实可行。
我已经创建了高阶路由,目前正在Switch
中使用它们,并且似乎可以正常工作,但这无法通过我公司的代码审查,因为它使用的是不受支持的API,随时休息。我想知道是否存在一种完全支持的方式来使用高阶路由和全部路由。
我正在研究尝试创建404路由而不需要使用Switch
,或者使用常规的Route
组件,但是在身份验证中包装了传递给Route
的组件逻辑。
import { Router, Route, Switch } from 'react-router-dom';
// Array of authorization functions to be tested by private routes
const authCriteria = [
// The next route must have been referred by a previous route
props => defined(props.location.state.from),
];
// Seems to work as expected, but violates the supported use
// of the Switch component
const AppRouter = () => {
return (
<Router>
<Switch>
<Route exact path='/' component={Store} />
<PrivateRoute
exact
path='/account'
component={Account}
authCriteria={authCriteria}
/>
<PrivateRoute
exact
path='/shipping'
component={Shipping}
authCriteria={authCriteria}
/>
<PrivateRoute
exact
path='/checkout'
component={Checkout}
authCriteria={authCriteria}
/>
// Catch-all route means we need to use a Switch
<Route render={() => (<h2>Not Found</h2>)} />
</Switch>
</Router>
);
};
export default AppRouter;
答案 0 :(得分:2)
根据您问题的链接到react-router的github的链接,看来您可以在Switch
内使用HOC。
但是,您可以实现类似的目标。基本上,您需要两个路由器。第一个Router
会路由所有预先验证的路由,例如登录,新用户,忘记密码等。由于它位于Switch
中,因此它应该呈现您的第二个Router
。您的第二个Router
将包含您所有需要身份验证的路由。但是,您需要包括条件检查以确保用户已通过身份验证。如果用户未通过身份验证,则只需将Redirect
返回到/login
路径即可。这是因为第一个Switch
有效。如果用户使用未经身份验证的用户无法访问的网址,则该用户将始终被重定向到/login
,因为他们将在第一开关和第二开关的Redirect
中达到默认条件组件。
import { Router, Route, Switch } from 'react-router-dom';
const AppRouter = (props) => {
return (
<Router>
<Switch>
<Route exact path='/login' component={Login} />
<Route exact path='/new-user' component={CreateAccount} />
<Route render={props => <AuthenticationRouter {...props} />}
</Switch>
</Router>
);
};
const AuthenticatedRouter = (props) => {
if( props.userIsAuthenticated ) {
return (
<Router>
<Switch>
<Route exact path='/' component={Store} />
<Route
exact
path='/account'
component={Account}
/>
<Route
exact
path='/shipping'
component={Shipping}
/>
<Route
exact
path='/checkout'
component={Checkout}
/>
<Route render={() => (<h2>Not Found</h2>)} />
</Switch>
</Router>
);
}
return (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
);
};
export default AppRouter;
答案 1 :(得分:1)
更新: 我更改了路由身份验证HoC,以包装路由呈现的组件,而不是路由本身。如果身份验证条件是静态的,则发布的第一个答案可能会更好,但是我正在使用的应用是下订单工作流,其中访问不同路线可能取决于不同的因素,例如完成或来自其他路线,因此我需要能够将任意身份验证标准传递给每个路由。
以下代码显示了我用来包装每个组件的withAuth
HoC以及它如何与路由器一起工作:
// Test every passed-in auth verification function.
const verifyAuth = (authCriteria, props) => {
if (authCriteria.length === 0) return true;
return authCriteria.every(criterion => criterion(props));
};
// Authentication HoC
const withAuth = ({
authCriteria = [],
redirectPath = '/',
} = {}) => Component => props => {
const isAuthorized = verifyAuth(authCriteria, props);
return (
isAuthorized ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: redirectPath,
state: { from: props.location },
}}
/>
)
);
};
// TODO: authenticate user
const validUser = _props => true;
// The next route must have been referred by a previous route
const internalReferral = props => defined(props.location.state);
// The Store route has different authentication requirements
// than the other two routes
const storeCriteria = [validUser];
const mainCriteria = [validUser, internalReferral];
const authRoute = withAuth({ authCriteria: mainCriteria });
const ProtectedRoutes = {
Store: withAuth({ authCriteria: storeCriteria })(Store),
Shipping: authRoute(Shipping),
Checkout: authRoute(Checkout),
};
const AppRouter = () => {
return (
<Router>
<Switch>
<Route exact path='/' component={ProtectedRoutes.Store} />
<Route exact path='/shipping' component={ProtectedRoutes.Shipping} />
<Route exact path='/checkout' component={ProtectedRoutes.Checkout} />
<Route render={() => (<h2>Not Found</h2>)} />
</Switch>
</Router>
);
};