问题
I'm using react-router to set up my application, and try to use <Redirect/>
to set router for authentication.
Route
component have two different components, one is private Route
, another is public route
.
Expect result : when auth is false, the page should jump back to a public page, which I set <Redirect to={"/public"} />
so far it seems route works fine, but redirect doesn't work properly.
Any ideas are welcome! Thanks!!
PrivateRoute
interface PrivateRouteProps {
isLogin: boolean;
privateRoutes: RouteItem[];
}
const PrivateRoute: React.FunctionComponent<PrivateRouteProps> = (
props: PrivateRouteProps
) => {
return (
<>
{props.isLogin ? (
props.privateRoutes.map(item => {
return <Route key={item.path} {...item} />;
})
) : (
<Redirect to={PUBLIC.path} />
)}
</>
);
};
PublicRoute
interface PublicProps {
publicRoutes: RouteItem[];
}
const PublicRoute: React.FC<PublicProps> = (props: PublicProps) => {
return (
<>
{props.publicRoutes.map(route => (
<Route key={route.path} {...route} />
))}
</>
);
};
Route
<BrowserRouter>
<Switch>
<PublicRoute publicRoutes={publicRoutes} />
<PrivateRoute privateRoutes={privateRoutes} isLogin={login} />
</Switch>
</BrowserRouter>
UPDATE
As the accepted answer mentioned, it's all about <Switch/>
works with Fragment, so I modified my routes as following, it works like a charm.
Just update it for someone may have similar question.
<BrowserRouter>
<Switch>
{publicRoutes.map(item => {
return <Route key={item.path} {...item}/>
})}
{privateRoutes.map(item => {
return <PrivateRoute key={item.path}
exact={item.exact}
component={item.component}
path={item.path}
redirectPath={SIGN_IN.path}
/>
})}
</Switch>
</BrowserRouter>
回答1:
I have gone through your code and boils down to one thing. The way that the component <Switch>
works with fragment <></>
. It only looks for the first React Fragment because they do not want to transverse a tree:
https://github.com/ReactTraining/react-router/issues/5785
To solve that you need to either remove the React.Fragment inside your components. So your application will look like:
<Switch>
<Route ...>
<Route ...>
<Route ...>
</Switch>
and NOT (btw that is how it is now)
<Switch>
//it will only sees the first one //if you switch orders - Private with Public
// Private will work but not Public anymore :(
<React.Fragment>
<Route ...>
<Route ...>
</React.Fragment>
<React.Fragment>
<Route ...>
</React.Fragment>
</Switch>
Another solution (that is what I did because I am not well versed in TypeScript enough to change types and returns) is to add a wrapper in your switch application and deal with the return of the private Routes using the render method inside the <Route>
as demonstrated below:
//index.tsx
<Switch>
<>
<PublicRoute publicRoutes={publicRoutes}/>
<PrivateRoute privateRoutes={privateRoutes} isLogin={login}/>
</>
</Switch>
That leads to another error of infinite loops re-renders (again the react-router is probably having a bad time with the nested routes) and to solve that you would do the following to your PrivateRoutes component:
//PrivateRoute.tsx
return (
<>
{props.privateRoutes.map(item =>
<Route key={item.path} exact path={item.path} render={() => (
!props.isLogin
? (
<Redirect to={PUBLIC.path}/>
):
//HOC transforming function Component into Component
// @ts-ignore (you can deal here better than me hehehe)
((PrivateComponent)=><PrivateComponent/>)(item.component)
)}/>)}
</>
);
TL,DR: You are adding nesting complexity by adding <></>
(translates to React.Fragment) inside your structure. If you remove them or follow the code above you should be fine
Hope I have helped it you. Good luck! :)
来源:https://stackoverflow.com/questions/57635090/react-router-redirect-doesnt-change-url-in-switch