Я подготовил простой тестовый пример на Github для моего вопроса:
В RouterLayout.jsx медиа-запрос заставляет Outlet исчезать на узких экранах, как вы можете видеть на анимированном снимке экрана, где я перетаскиваю сторону браузера, чтобы уменьшить его:
RootLayout.jsx
import { Outlet } from "react-router";
import { useMediaQuery } from "@react-hook/media-query";
import MasterList from "../components/MasterList";
export default function RootLayout() {
const isSmallScreen = useMediaQuery("(max-width: 768px)");
return (
<div
style = {{
display: "flex",
justifyContent: "space-around",
alignItems: "flex-start",
}}
>
<MasterList />
{!isSmallScreen && (
<div>
<h3>Detail</h3>
<Outlet />
</div>
)}
</div>
);
}
Мой вопрос, пожалуйста:
На маленьких экранах мобильных телефонов должен отображаться только основной список. И когда там щелкают ссылку, должна отображаться страница 1 или другие страницы с возможностью вернуться обратно в основной список. Это пока не работает.
На больших экранах планшетов и компьютеров основной список и страница 1 или другие отображаются рядом друг с другом. Это уже работает в моем коде.
Должен ли я добавить какой-нибудь атрибут к Link в файле MasterList.jsx ниже?
MasterList.jsx
import { Link } from "react-router-dom";
const MasterList = () => {
const pages = [
{ id: 1, title: "Page 1" },
{ id: 2, title: "Page 2" },
{ id: 3, title: "Page 3" },
{ id: 4, title: "Page 4" },
];
return (
<div>
<h2>Master List</h2>
<nav>
<ul>
{pages.map((page) => (
<li key = {page.id}>
<Link to = {"/page" + page.id}>{page.title}</Link>
</li>
))}
</ul>
</nav>
</div>
);
};
export default MasterList;
Приложение.jsx
import {
Route,
RouterProvider,
createBrowserRouter,
createRoutesFromElements,
} from "react-router-dom";
import Page1 from "./pages/Page1";
import Page2 from "./pages/Page2";
import Page3 from "./pages/Page3";
import Page4 from "./pages/Page4";
import RootLayout from "./layouts/RootLayout";
const router = createBrowserRouter(
createRoutesFromElements(
<Route path = "/" element = {<RootLayout />}>
<Route path = "page1" element = {<Page1 />} />
<Route path = "page2" element = {<Page2 />} />
<Route path = "page3" element = {<Page3 />} />
<Route path = "*" element = {<Page4 />} />
</Route>
)
);
const App = () => {
return <RouterProvider router = {router} />;
};
export default App;
Пожалуйста, измените заголовок, чтобы задать что-то более конкретное. Как создать сложное приложение — очень обширная тема. Ограничьте круг вашей актуальной проблемой, чтобы нам всем не приходилось бродить сюда, чтобы выяснить, что вам действительно нужно. См. Как спросить.





Я не знаю, какой лучший подход для достижения этой цели, но он должен учитывать тот факт, что Outlet отображает дочерние маршруты в родительском компоненте.
Я бы изучил useMatches, чтобы узнать, можно ли использовать его для определения того, был ли родительский компонент отображен на /parent или /parent/child. С помощью этой информации вы можете условно визуализировать элементы навигации:
При меньших размерах представления, где Outlet не отображается, у вложенных Route компонентов больше нет места для отображения element содержимого. Если я правильно понимаю ваше сообщение, вы хотите использовать RootLayout и Outlet в больших размерах просмотра, не «мобильных», а в размерах просмотра для мобильных устройств отображать RootLayout как родственный маршрут вместо родительского маршрута, чтобы все остальные маршруты были доступны.
Пример рефакторинга:
Создайте два корневых макета: мобильный и немобильный.
function RootLayout() {
return (
<div
style = {{
display: "flex",
justifyContent: "space-around",
alignItems: "flex-start"
}}
>
<MasterList />
<div>
<h3>Detail</h3>
<Outlet />
</div>
</div>
);
}
function MobileLayout() {
return (
<div
style = {{
display: "flex",
justifyContent: "space-around",
alignItems: "flex-start"
}}
>
<div>
<h3>Detail</h3>
<Outlet />
</div>
</div>
);
}
Обновите App, чтобы использовать хук медиа-запроса и условно отображать тот или иной корневой макет, а также условно отображать MasterList как индексный маршрут на экранах меньшего размера.
export default function App() {
const isSmallScreen = useMediaQuery("(max-width: 768px)");
const router = React.useMemo(
() =>
createBrowserRouter(
createRoutesFromElements(
<Route
path = "/"
element = {isSmallScreen ? <MobileLayout /> : <RootLayout />}
>
{isSmallScreen && <Route index element = {<MasterList />} />}
<Route path = "page1" element = {<Page1 />} />
<Route path = "page2" element = {<Page2 />} />
<Route path = "page3" element = {<Page3 />} />
<Route path = "*" element = {<Page4 />} />
</Route>
)
),
[isSmallScreen]
);
return <RouterProvider router = {router} />;
}
Альтернативой может быть использование хука useMatch, чтобы проверить, является ли корень "/" текущим совпадающим маршрутом, и отрендерить MasterList. Логика немного более запутанная и немного менее СУХАЯ. Суть здесь в том, чтобы отображать обычные MasterList и Outlet для больших размеров представления, а в мобильных представлениях условно отображать MasterList, только если на домашней странице, в противном случае визуализируйте Outlet для вложенных маршрутов.
Пример:
import { Outlet, useMatch } from 'react-router-dom';
...
function RootLayout() {
const isSmallScreen = useMediaQuery("(max-width: 768px)");
const isHome = useMatch("/");
return (
<div
style = {{
display: "flex",
justifyContent: "space-around",
alignItems: "flex-start"
}}
>
{isSmallScreen ? (
isHome ? (
<MasterList />
) : (
<div>
<h3>Detail</h3>
<Outlet />
</div>
)
) : (
<>
<MasterList />
<div>
<h3>Detail</h3>
<Outlet />
</div>
</>
)}
</div>
);
}
const router = createBrowserRouter(
createRoutesFromElements(
<Route path = "/" element = {<RootLayout />}>
<Route path = "page1" element = {<Page1 />} />
<Route path = "page2" element = {<Page2 />} />
<Route path = "page3" element = {<Page3 />} />
<Route path = "*" element = {<Page4 />} />
</Route>
)
);
export default function App() {
return <RouterProvider router = {router} />;
}
Выходы используются для визуализации дочерних маршрутов, поэтому имеет смысл, чтобы дочерний компонент не отображался, если вы не визуализируете выход. Можете ли вы более четко описать, чего вы пытаетесь достичь? Что и по какому пути вы хотите отображать на маленьких или больших экранах?