Я создаю приложение для реагирования, и я не могу заставить работать маршрутизацию.
Мне нужен один общий макет (верхний и нижний колонтитулы) для нескольких маршрутов аутентификации (/login, sign-up, forgot-password и т. д.)
И мне нужен другой общий макет для остальных защищенных частей приложения (Home, Dashboard и т. д.)
Мне нужна еще одна страница 404 без макета.
Я пробовал несколько методов из этих ссылок:
Но смог дойти до рабочей версии.
Это то, что у меня сейчас есть:
(Примечание: на данный момент я игнорирую необходимость блокировать незарегистрированных пользователей в частных маршрутах AppLayout, я займусь этим сразу после)
const App: React.FC = () => {
const history = createBrowserHistory();
return (
<div className = "App">
<Router history = {history}>
<Switch>
<AppLayout>
<Route path = "/home" component = {HomePage}/>
<Route path = "/dashboard" component = {DashboardPage}/>
...
</AppLayout>
<AuthLayout>
<Route path = "/login" component = {LoginPage}/>
<Route path = "/sign-up" component = {SignUpPage}/>
...
</AuthLayout>
<Route path = "*" component = {NotFoundPage} />
</Switch>
</Router>
</div>
);
};
export default App;
И AuthLayout, и AppLayout просты и похожи на это (только с разными верхним/нижним колонтитулом для каждого):
class AppLayout extends Component {
render() {
return (
<div className = "AppLayout">
<header>...</header>
{this.props.children}
<footer>...</footer>
</div>
);
}
}
export default AppLayout;
Проблема в том, что рендерятся только маршруты из AppLayout.
Другие маршруты просто показывают AppLayout header и footer без какого-либо содержимого.
Это версии реакции, которые я использую:
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-router-dom": "^5.0.0",
Любая помощь будет оценена по достоинству.
Заранее спасибо.





Вы можете попробовать использовать два разных оператора switch для обработки маршрутов Auth и Protected. У меня был аналогичный вариант использования на работе, и мне было удобно иметь два набора блоков переключателей, из которых только один работал одновременно.
const App: React.FC = () => {
const history = createBrowserHistory();
return (
<div className = "App">
<Router history = {history}>
{isLoggedIn ? <PrivateRoutes /> : <AuthRoutes />}
</Router>
</div>
);
};
const PrivateRoutes: React.FC = () => {
return (
<>
<Header />
<Switch>
<Route path = "/home" component = {HomePage} />
<Route path = "/dashboard" component = {DashboardPage} />
<Route path = "*" component = {NotFoundPage} />
</Switch>
<Footer />
</>
);
};
const AuthRoutes: React.FC = () => {
return (
<>
<Header />
<Switch>
<Route path = "/login" component = {LoginPage} />
<Route path = "/sign-up" component = {SignUpPage} />
<Route path = "*" component = {NotFoundPage} />
</Switch>
<Footer />
</>
);
};
Каждый из ваших макетов должен иметь компонент пути, чтобы отличаться от других макетов.
Например
Макеты аутентификации могут находиться под /auth, например, вход будет /auth/login, регистрация будет /auth/signup
Макет приложения может находиться под /app, например, панель инструментов будет /app/dashboard, дом будет /app/home
import { Switch, BrowserRouter, Route, Redirect } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Layouts />
</BrowserRouter>
);
}
const NotFound = () => <h1>Not Found</h1>;
function Layouts() {
return (
<Switch>
<Route path = "/auth" component = {AuthLayout} />
<Route path = "/app" component = {AppLayout} />
<Route path = "/" component = {NotFound} />
</Switch>
);
}
const Signup = () => <p>Login</p>;
const Login = () => <p>Sign up</p>;
function AuthLayout() {
return (
<div>
<h1>Auth Layout</h1>
<Route path = "/auth/signup" exact component = {Signup} />
<Route path = "/auth/login" exact component = {Login} />
<Redirect from = "/auth" to = "/auth/login" exact />
</div>
);
}
const Home = () => <p>Home</p>;
const Dashboard = () => <p>Dashboard</p>;
function AppLayout() {
return (
<div>
<h1>App Layout</h1>
<Route path = "/app/home" exact component = {Home} />
<Route path = "/app/dashboard" exact component = {Dashboard} />
<Redirect from = "/app" to = "/app/home" exact />
</div>
);
}
Кроме того, если вы хотите защитить определенные маршруты от отображения, если они не аутентифицированы, вы можете создать компонент PrivateRoute, который будет перенаправлять на макет аутентификации, если он не аутентифицирован.
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render = {props => sessionStorage.token // your auth mechanism goes here
? <Component {...props} />
: <Redirect to = {{ pathname: '/auth' }} />}
/>
);
Вы можете использовать этот компонент PrivateRoute вместо компонента react-routerRoute.
Например:
<PrivateRoute path = "/app" component = {AppLayout} />
Спасибо! хотя я предпочитаю решение, не ограничивающее меня в изменении URL-адресов (/app,/auth)
Это решение помогло мне разобраться. Это лучше, чем принятый ответ imo.
Как человек с опытом работы с angular, это самый разумный и чистый подход к маршрутизации, который я когда-либо видел в React. Слишком много тарабарщины в подходах к маршрутизации React. Это чисто, читабельно, очевидно и легко расширяется. Спасибо
если вы переходите к чему-то вроде /app/somethingnotfound, это не работает, это не переходит к компоненту NotFound
@Vencovsky, удалив точный маршрут из корневого макета, вы сможете перейти к /app/something
Обновлено: Я ответил на этот вопрос другим решением.
Оба решения от @Gaurab Kc и @johnny peter великолепны, хотя в итоге я сделал что-то вроде этого:
<Router history = {history}>
<Switch>
<PrivateRoute
path = "/home"
component = {HomePage}>
</PrivateRoute>
<PrivateRoute
path = "/dashboard"
component = {DashboardPage}>
</PrivateRoute>
<AuthRoute
path = "/login"
component = {LoginPage}>
</AuthRoute>
<AuthRoute
path = "/sign-up"
component = {SignUpPage}>
</AuthRoute>
<Route path = "*" component = {NotFoundPage}/>
</Switch>
</Router>
AuthRoute и PrivateRoute примерно так:
interface PrivateRouteProps extends RouteProps {
component: any;
}
const PrivateRoute = (props: PrivateRouteProps) => {
const {component: Component, ...rest} = props;
return (
<Route
{...rest}
render = {(routeProps) =>
localStorage.getItem('user') ? (
<div>
... // here is the app header
<Component {...routeProps} />
.. // here is the app footer
</div>
) : (
<Redirect
to = {{
pathname: '/login',
state: {from: routeProps.location}
}}
/>
)
}
/>
);
};
export default PrivateRoute;
interface AuthRouteProps extends RouteProps {
component: any;
}
const AuthRoute = (props: AuthRouteProps) => {
const {component: Component, ...rest} = props;
return (
<Route
{...rest}
render = {(routeProps) =>
(
<div>
... // here is the auth header
<Component {...routeProps} />
.. // here is the auth footer
</div>
)
}
/>
);
};
export default AuthRoute;
Этот подход совершенно неверен, потому что он побеждает всю цель макетов в SPA. Ваш макет содержит элементы пользовательского интерфейса, общие для определенных экранов, которые не отображаются повторно при изменении маршрутов в макете. В вашем подходе, когда вы переключаетесь между разными маршрутами в одном и том же макете, весь ваш общий пользовательский интерфейс также уничтожается и перерисовывается, нарушая всю цель общего заголовка и нижнего колонтитула.
@johnnypeter Это правильный и важный момент. Но на данном этапе я бы предпочел заплатить за решение, которое заставляет меня устанавливать маршруты /auth и /app. Буду рад узнать, есть ли другое решение. Спасибо.
Мне потребовалось некоторое время, чтобы найти ответ, который мне понравился. Оба решения @johnny-peter и @gaurab-kc были великолепны, и они рассказали мне о механизме маршрутизации React.
Решение @johnny-peter имело тот недостаток, что вынуждало меня ставить префикс для всех связанных маршрутов auth под /auth/... (например, /auth/login и auth/sign-up), чего я не хотел.
Решение @gaurab-kc поддерживало только один набор маршрутов.. поэтому, если пользователь уже был зарегистрирован, он больше не мог посещать маршрут /login.
До недавнего времени я использовал свое собственное решение, у которого была проблема «победить всю цель общего верхнего и нижнего колонтитула». как упомянул @johnny-peter, и за него несколько раз проголосовали, как и должно быть.
Теперь я использую другое решение:
<Router history = {browserHistory}>
<Switch>
<Redirect exact from = "/" to = "/home"/>
<Route exact path = {["/login", "/sign-up", ...]}>
<AuthLayout>
<Switch>
<Route
path = "/login"
component = {LoginPage}
/>
<Route
path = "/sign-up"
component = {SignUpPage}
/>
</Switch>
</AuthLayout>
</Route>
<Route exact path = {[
"/home",
"/dashboard",
...
]}>
<SiteLayout>
<Switch>
<Route
path = "/home"
component = {HomePage}
/>
<Route
path = "/dashboard"
component = {DashboardPage}
/>
</Switch>
</SiteLayout>
</Route>
<Route path = "*" component = {NotFoundPage}/>
</Switch>
</Router>
что предотвращает все вышеперечисленные недостатки. Это позволяет мне:
/login или другим auth маршрутам без предварительного выхода из системы.Единственным недостатком этого решения является наличие большего количества кода и дублирование маршрутов, но это цена, которую я готов заплатить.
Это хорошее решение! Спасибо! В итоге я использовал другое решение, которое я расскажу в своем собственном ответе, но я многому научился у вас!