Перейти к основному содержимому

Лейауты страниц

Это руководство рассматривает абстракцию лейаута страницы — когда несколько страниц имеют одинаковую структуру, отличаясь только основным содержимым.

к сведению

Вашего вопроса нет в этом руководстве? Напишите свой вопрос, оставив отзыв к этой статье (синяя кнопка справа), и мы рассмотрим возможность расширения этого руководства!

Простой лейаут

Самый простой лейаут можно увидеть прямо на этой странице. Он имеет хэдер с навигацией по сайту, два сайдбара и футер с внешними ссылками. Здесь нет сложной бизнес-логики, и единственные динамические части — это сайдбары и переключатели справа в хэдере. Такой лейаут можно разместить целиком в shared/ui или в app/layouts, с заполнением контента сайдбаров через пропы:

shared/ui/layout/Layout.tsx
import { Link, Outlet } from "react-router-dom";
import { useThemeSwitcher } from "./useThemeSwitcher";

export function Layout({ siblingPages, headings }) {
const [theme, toggleTheme] = useThemeSwitcher();

return (
<div>
<header>
<nav>
<ul>
<li> <Link to="/">Home</Link> </li>
<li> <Link to="/docs">Docs</Link> </li>
<li> <Link to="/blog">Blog</Link> </li>
</ul>
</nav>
<button onClick={toggleTheme}>{theme}</button>
</header>
<main>
<SiblingPageSidebar siblingPages={siblingPages} />
<Outlet /> {/* Здесь будет основное содержимое страницы */}
<HeadingsSidebar headings={headings} />
</main>
<footer>
<ul>
<li>GitHub</li>
<li>Twitter</li>
</ul>
</footer>
</div>
);
}
shared/ui/layout/useThemeSwitcher.ts
export function useThemeSwitcher() {
const [theme, setTheme] = useState("light");

function toggleTheme() {
setTheme(theme === "light" ? "dark" : "light");
}

useEffect(() => {
document.body.classList.remove("light", "dark");
document.body.classList.add(theme);
}, [theme]);

return [theme, toggleTheme] as const;
}

Код сайдбаров оставлен читателю в качестве упражнения 😉.

Использование виджетов в лейауте

Иногда есть необходимость включить в лейаут определенную бизнес-логику, особенно если вы используете глубоко вложенные маршруты с роутером типа React Router. Тогда вы не можете хранить лейаут в Shared или в Widgets из-за правила импорта для слоёв:

Модуль в слайсе может импортировать другие слайсы только в том случае, если они расположены на слоях строго ниже.

Прежде чем обсуждать решения, нам нужно обсудить, действительно ли это проблема. Вам действительно нужен этот лейаут, и если да, действительно ли он должен быть виджетом? Если блок бизнес-логики, про который идёт речь, используется на 2-3 страницах, и лейаут просто является небольшой обёрткой для этого виджета, рассмотрите один из этих двух вариантов:

  1. Напишите ваш лейаут прямо в коде роутера на уровне App
    Это отлично подходит для роутеров, поддерживающих вложенность, потому что вы можете группировать определенные маршруты и применять нужный лейаут только к ним.

  2. Просто скопируйте его
    Желание абстрагировать код часто переоценено. Это особенно верно для лейаутов, потому что они редко меняются. В какой-то момент, если одна из этих страниц потребует изменений, вы можете просто внести изменения, не затрагивая другие страницы. Если вы беспокоитесь, что кто-то может забыть обновить другие страницы, всегда можно оставить комментарий, описывающий отношения между страницами.

Если ни один из вышеперечисленных вариантов не подходит, есть два решения для включения виджета в лейаут:

  1. Используйте render props или слоты
    Большинство фреймворков позволяют передавать часть UI внешне. В React это называется render props, в Vue — слоты.

  2. Переместите лейаут на уровень App
    Вы также можете хранить свой лейаут на уровне App, например, в app/layouts, и комбинировать любые виджеты, которые вам нужны.

Дополнительные материалы

  • Пример создания лейаута с аутентификацией с помощью React и Remix (аналогичен React Router) можно найти в туториале.