Для создания интерфейсов React рекомендует использовать композицию и библиотеки по управлению состоянием (state management libraries) для построения иерархий компонентов. Однако при сложных паттернах композиции появляются проблемы: Для большинства разработчиков проблема может быть неочевидна, и они перекидывают ее на уровень управления состоянием. Это обсуждается и в документации React: Если пройти по ссылке, мы увидим еще один аргумент: Конечно, если бы каждый, кто использует инструмент, читал документацию и признавал авторитет авторов, то этой публикации не было. Поэтому разберем проблемы существующих подходов.  Я начинал с этого решения из-за фреймворка Vue, который рекомендует такой подход. Берем структуру данных, которая приходит с бэка или дизайна в случае формы. Пробрасываем в наш компонент – например, в карточку фильма: Стоп. Мы уже знаем о бесконечном расширении требований к компоненты. Вдруг у названия будет ссылка на рецензию фильма? А у жанра – на лучшие фильмы из него? Не добавлять же теперь: Так мы обезопасим себя от проблем в будущем, но открываем двери ошибке нулевого поинтера. Изначально мы могли прокидывать структуры прямиком из бэка: Теперь каждый раз нужно дублировать всю информацию: Однако мы забыли про жанр – и компонент упал. А если не поставили ограничители ошибок, то с ним и все приложение. На помощь приходит TypeScript. Упрощаем схему с помощью рефакторинга карточки и элементов, которые ее используют. Благо, все подсвечивается в редакторе или при сборке: Чтобы сэкономить время, все равно прокидываем данные «as any» или «as IMovieCardProps». Что же получается? Мы уже три раза (если используем в одном месте) описали одну структуру данных. И что имеем? Компонент, который до сих пор нельзя модифицировать. Компонент, который потенциально может обвалить все приложение. Пришло время переиспользовать этот компонент. Рейтинг больше не нужен. У нас два варианта: Пропс withoutRating ставьте везде, где не нужен рейтинг Быстро, но мы нагромождаем пропсы и сооружаем четвертую структуру данных. Делаем rating в IMovieCardProps необязательным. Не забываем сделать его пустым объектом по умолчанию Хитрее, но становится сложно читать код. Опять же, повторяемся четвертый раз. Контроль над компонентом не очевиден, так как непрозрачно управляется структурой данных. Скажем, нас попросили сделать пресловутый рейтинг ссылкой, но не везде: И тут упираемся в сложную логику, которую диктует непрозрачная структура данных.  Одновременно странный и популярный подход. Я использовал его, когда начал работать с React и функционала JSX во Vue стало не хватать. Не раз слышал от разработчиков на митапах, что такой подход позволяет пропускать более обобщенные структуры данных: Естественно, к проблеме непрозрачности (1) добавляется проблема перегруженности логикой (2) и добавление состояния в финальный компонент (3). Последнее (3) диктуется внутренней сохранностью объекта. То есть глубинно проверяем объекты через lodash.isEqual. Если случай продвинут или JSON.stringify, все только начинается. Еще можно добавить timestamp и проверять по нему, если все потеряно. Надобность в сохранении или мемоизации отпадает, так как по сложности вычисления оптимизация может оказаться сложнее редюсера. Данные пробрасываются с названием сценария (как правило, строкой): Теперь пишем компонент: И логику: На этом шаге возникает несколько проблем:  Тут мы отказываемся от шины данных для построения интерфейсов, которой является React. Используем технологию с собственной моделью логики. Вероятно, это самый распространенный способ создания приложений на React, хоть руководство предупреждает, что не стоит использовать контекст таким образом. Используйте подобные инструменты там, где React не предоставляет достаточного инструментария – например, в маршрутизации. Скорее всего, вы используете react-router. В этом случае для пробрасывания сессии во все страницы больший смысл имело бы использование контекста, а не пробрасывания коллбэка из компонента каждого маршрута верхнего уровня. В React нет отдельной абстракции для асинхронных действий кроме той, что предлагает язык Javascript. Казалось бы, есть плюс: можем переиспользовать логику в будущих версиях приложения. Но это обман. С одной стороны, она привязана к API, с другой – к структуре приложения, а логика обеспечивает эту связь. При изменении одной из частей, ее требуется переписывать.  Метод композиции очевиден, если следовать следующим принципам (не считая аналогичного подхода в книге Design Patterns): Поэтому переводите данные из одного домена в другой как можно раньше. В React для шаблонизации HTML используется абстракция JSX, а на деле – набор методов createElement. То есть, к JSX и компонентам React, которые тоже являются элементами JSX, стоит относиться как к методу отображения и поведения, а не трансформации и обработки данных, которые должны происходить на отдельном уровне. На этом шаге многие и используют способы, перечисленные выше, но они не решают ключевую проблему расширения и модификации отображения компонентов. Как это делать по мнению создателей библиотеки, показано в документации: То есть, в качестве параметров вместо строк, чисел и булевых типов передаются уже готовые, сверстанные компоненты. Увы, этот способ тоже оказался негибким. Вот почему: В итоге, подобное решение может разрастись в сложности даже для достаточно простых сценариев: В документации подобный код, в случае компонентов высшего порядка (HOC) и рендер-пропсов называют «адом оберток» (wrapper hell). С каждым добавлением нового элемента читаемость кода становится все сложнее. Один ответ на эту проблему — слоты — присутствует в технологии Веб-компонентов и в фреймворке Vue. В обеих местах, однако, присутствуют ограничения: во-первых, слоты определены не символом, а строкой, что усложняет рефакторинг. Во-вторых, слоты ограничены по функционалу и не могут сами управлять своим отображением, передавать дочерним компонентам другие слоты или быть переиспользоваными в других элементах. Если коротко, примерно вот такое, назовем его паттерн №5 – слоты: Об этом расскажу в следующей статье о существующих решениях для слотового паттерна в React и о моем собственном решении. Frontend Engineer