Что такое паттерн React?
Паттерн React — это переиспользуемое именованное решение повторяющейся задачи проектирования компонентов. Трек предполагает, что ты уже умеешь писать на React, и учит паттернам, которые держат компоненты простыми по мере роста приложения.
Ты прошёл трек React: знаешь хуки, модель рендеринга, эффекты, Suspense. Ты умеешь собрать фичу. Потом фича растёт — ещё три состояния, второй потребитель, дизайнер просит ещё один вариант — и компонент, чистый на 60 строках, превращается в 400-строчный клубок пропсов, флагов и эффектов, который все боятся трогать. API React тебя не подвёл. Подвела композиция.
Этот трек — про следующий слой: не что делает useState, а как расположить компоненты, состояние и границы, чтобы код оставался простым по мере роста приложения. У этого знания о расположении есть имя — паттерны, — и работа уровня senior в React во многом состоит в том, чтобы знать, какой паттерн просит ситуация, а какой — нет.
После этого урока ты можешь определить паттерн React как именованное переиспользуемое решение повторяющейся задачи проектирования компонентов; объяснить, почему паттерны важнее по мере роста приложения (сложность накапливается); прочитать карту трека как прогрессию от композиции к состоянию и границам; и применять один тест, отделяющий полезный паттерн от переусложнения, — делает ли он вероятное следующее изменение дешевле?
Фундамент, на котором стоит каждый паттерн React: UI — это функция состояния, UI = f(state). Ты не мутируешь DOM императивно; ты декларируешь, как должен выглядеть UI для текущего состояния, а React сверяет и обновляет. Каждый паттерн в этом треке — разный ответ на одни и те же два вопроса, вытекающих из этой модели: где живёт состояние? и как рендеринг составлен из него?
// вся модель в одной строке: дано состояние — опиши UI
function Counter({ count }: { count: number }) {
return <p>Count: {count}</p>;
}Держи это в голове: «композиция», «колокация состояния», «граница сервер/клиент» — не разрозненные трюки, а всё про то, как хорошо формировать f и располагать state.
Паттерн — это именованное переиспользуемое решение повторяющейся задачи, и имя — половина ценности. «Подними это состояние вверх», «сделай эти compound-компоненты», «вынеси кастомный хук», «поставь error boundary над этим Suspense» — каждое называет проблему, с которой ты встретишься не раз, и форму, которая её решает. Имя позволяет команде сказать что делать тремя словами и рассуждать о компромиссах, не выводя их заново. Паттерн — не библиотека и не сниппет для вставки; это проектное решение с известными последствиями.
Повторяющиеся задачи, которые решают паттерны в React, группируются в несколько семейств:
- Композиция — как компоненты соединяются (children, слоты, compound-компоненты, render-props).
- Состояние — где живёт состояние и как оно течёт (колокация, подъём, контекст, внешние сторы).
- Эффекты и производительность — синхронизация с внешним миром и отсутствие лишних перерисовок.
- Границы — сервер против клиента, Suspense, error boundaries, формы и actions, загрузка данных.
Паттерны важнее по мере роста приложения, потому что сложность накапливается. Приложение из 5 компонентов терпит почти любую структуру. На 500 компонентах плохой выбор — состояние, хранимое слишком высоко, так что перерисовывается всё; логика, продублированная по компонентам; божественный компонент, владеющий десятью обязанностями, — умножается в тысячи ненужных рендеров, запутанную связанность изменений и фичи, которые никто не может добавить безопасно. Паттерны — это то, как ты держишь стоимость следующего изменения плоской, а не позволяешь ей загибаться вверх по мере роста кода.
// плохо масштабируется: один компонент владеет загрузкой, фильтрацией,
// сортировкой, пагинацией, выбором и рендерингом — каждое изменение трогает всё
function UserTable() { /* 400 строк, 9 useState, 4 эффекта */ }
// хорошо масштабируется: каждая обязанность — составная часть с одной задачей
// <DataTable> + useUsers() + <Toolbar> + <Pagination>Паттерны не нужны, чтобы маленькое работало. Они нужны, чтобы большое оставалось изменяемым.
Senior-тест для любого паттерна: делает ли он вероятное следующее изменение дешевле — не переплачивая сейчас? У каждого паттерна есть цена: косвенность, больше файлов, концепция для изучения. Compound-API великолепен для виджета Tabs, переиспользуемого по всему приложению, и абсурден для одноразового ряда из двух кнопок. Контекст решает проброс пропсов и, применённый неверно, перерисовывает полдерева на каждое нажатие клавиши. Поэтому дисциплина не «применяй паттерны», а «подбирай паттерн под давление». Спроси: что здесь вероятно изменится или будет переиспользовано, и делает ли этот паттерн именно это дешёвым, оставаясь простым для всего остального?
Этот трек учит каждому паттерну вместе с его режимом отказа — когда он окупается, а когда это переусложнение, — чтобы ты тянулся к нужному, а не к самому вычурному.
Одна фича, два расположения — смотри, как паттерн оправдывает себя. «Выбор пользователя» нуждается в поле поиска и списке, разделяющих строку запроса.
Расположение A впихивает всё в один компонент и пробрасывает пропсы:
function UserPicker() {
const [q, setQ] = useState("");
const [selected, setSelected] = useState<string | null>(null);
// поле поиска, фильтрация, рендеринг списка, выбор — всё здесь
return (
<div>
<input value={q} onChange={(e) => setQ(e.target.value)} />
<ul>{/* фильтрация + map + логика выбора инлайн */}</ul>
</div>
);
}Сегодня нормально. Но когда второму экрану понадобится тот же выбор с другой раскладкой списка, A не переиспользовать без копипасты. Расположение B применяет композицию + колокацию состояния: общее состояние сидит в родителе, части составные.
function UserPicker({ children }: { children: React.ReactNode }) {
const [q, setQ] = useState("");
return <PickerContext.Provider value={{ q, setQ }}>{children}</PickerContext.Provider>;
}
// использование — разметка меняется по экранам, поведение общее
<UserPicker>
<UserPicker.Search />
<UserPicker.List render={(u) => <CompactRow user={u} />} />
</UserPicker>B стоит дороже заранее: контекст, compound-API. Эта цена оправдана только потому, что переиспользование-с-разной-разметкой — вероятное следующее изменение. Если бы это было не так, A был бы выбором уровня senior. Это суждение — паттерн, подобранный под реальное давление, — и есть вся игра, а остальной трек — это каталог паттернов и давлений, на которые каждый отвечает.
▸Почему это работает
Почему не выучить просто библиотеки (Redux, TanStack Query, набор компонентов) вместо паттернов? Потому что библиотеки реализуют паттерны, и чтобы выбрать или настроить одну хорошо, нужно понимать паттерн под ней. TanStack Query — это паттерн серверного состояния; Zustand — паттерн внешнего стора; Radix — compound-компоненты + headless-поведение. Зная паттерн, ты можешь оценить любую библиотеку, которая его обещает, заменить её или написать 20-строчную версию сам, когда зависимость не стоит того. Паттерны переживают библиотеки.
▸Частая ошибка
Самое частое неверное прочтение «выучи паттерны» — «применяй паттерны везде». Тянуться к compound-API, контексту, кастомному хуку и memo на компоненте с одним состоянием и одним потребителем — это не senior, это та же проблема сложности в более вычурном пальто. Паттерны — инструменты, подобранные под давление. Простой useState в простом компоненте — правильный ответ гораздо чаще, чем ожидают новички этого трека. Каждый урок здесь называет, когда не применять свой паттерн, именно по этой причине.
Коллега оборачивает одноразовый ряд из двух кнопок (используется на одном экране, переиспользование не планируется) в compound-API с context-провайдером. По определению этого трека, это хорошее применение паттерна?
Паттерн React — это именованное переиспользуемое решение повторяющейся задачи проектирования компонентов. Каждый паттерн в этом треке происходит из одной модели — UI = f(state) — и отвечает на один из её вопросов: как составлен рендеринг, где живёт состояние, как ведут себя эффекты и производительность, или как проведены границы (сервер/клиент, Suspense, данные, формы). Паттерны важнее по мере роста приложения, потому что сложность накапливается: правильное расположение держит стоимость следующего изменения плоской. Но у каждого паттерна есть цена, поэтому senior-тест никогда не «применяй паттерн» — это «делает ли это вероятное следующее изменение дешевле, не переплачивая сейчас?» Этот трек проходит семейства именно в таком порядке, каждый паттерн — с его режимом отказа, чтобы ты выучил не только паттерны, но и суждение, когда к ним тянуться. Ты уже знаешь React; теперь научись его располагать.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.