Когда вы начнёте спрашивать про AJAX и React, первое что ответят специалисты: React - это библиотека-представление (буква V в аббревиатуре MVC) и в React нет сетевых/AJAX особенностей.
Это нужно знать, но это не очень полезно, когда вы просто хотите получить данные с севера и отобразить их в ваших React компонентах.
Правда, есть множество способов сделать это. Вероятно, что вы сами обдумывали некоторые из них, но если вы выберите неправильный путь, ваш код может стать грязным.
Таким образом вы задаётесь вопросом: Какой из путей "правильный" или "предпочтительный"?
Какая практика получения данных с сервера в ваши React компоненты наиболее успешная?
И ответ на этот вопрос... в зависимости от обстоятельств.
Четыре способа
Я собрал 4 хороших способа использования AJAX с React.
Какой из подходов подойдёт вам зависит от размера и сложности вашего приложения, а также от библиотек и технологий, которые вы уже используете.
1. Корневой компонент | 2. Контейнерные компоненты | 3. Redux Async Actions | 4. Relay |
1. Корневой компонент
Это самый простой способ, который прекрасно подходит для прототипов и небольших приложений.
При таком подходе вы создаёте один корневой (родительский) компонент, который отвечает за все AJAX запросы. Корневой компонент хранит все данные AJAX ответов в своём состоянии и передаёт это состояние (или его часть) в дочерние компоненты в качестве свойств.
Для примера можно взглянуть в официальное руководство по React. Компонент CommentBox - это корневой компонент, который отправляет все AJAX запросы.
Что мне не нравится в официальном учебнике, дак это то, что они используют jQuery для отправки AJAX запросов. jQuery - это большая библиотека с большим количеством функций, поэтому использовать её только для AJAX не имеет смысла.
Я рекомендую использовать fetch(). Это простой, стандартизированный, JavaScript AJAX API. Он уже поддерживается Chrome и Firefox, а также есть полифиллы для Node и других браузеров. Подробности вы можете узнать в статье про сравнение AJAX библиотек.
Ещё один нюанс: Если у вас есть глубокое дерево компонентов (subcomponents of subcomponents of subcomponents...), тогда вам придётся передавать данные по длинному пути от корневого компонента к более глубоким компонентам.
Когда использовать корневой компонент:
- У вас не глубокое дерево компонентов;
- Вы не используете Redux или Flux;
2. Контейнерные компоненты
Компонент-контейнер предоставляет данные и поведение презентационным компонентам или другим компонентам-контейнерам. Если вы ещё не слышали про этот термин, то я предлагаю вам прочитать статью о презентационных и контейнерных компонентах от Dan Abramov.
В нашем случае, подход с контейнерными компонентами выглядит также как и подход с корневым контейнером, за исключением того, что здесь несколько компонентов могут взаимодействовать с сервером.
Вот как это работает: Для каждого презентационного компонента, которому нужны данные с сервера, создаётся контейнерный компонент, который отправляет AJAX запросы для получения этих данных, и передаёт их дочернему компоненту через свойства.
В качестве конкретного примера, представьте, что мы хотим отобразить профиль пользователя с его именем и фотографией.
Во первых нам нужно построить презентационный компонент <UserProfile />
который принимает в качестве свойств name
и profileImage
. В этом компоненте не должно быть никакого AJAX кода.
Затем нам нужно разработать компонент <UserProfileContainer />
, который принимает userId
. Он загружает данные для конкретного пользователя и передаёт их через свойства в компонент <UserProfile />
.
AJAX запросы в контейнерных компонентах могут быть отправлены с помощью простой библиотеки. Я рекомендую fetch().
Когда использовать контейнерные компоненты:
- У вас глубокое дерево компонентов;
- Многие из ваших компонентов не требуют данных с сервера, но некоторым они нужны;
- Вы получаете данные из нескольких API;
- Вы не используете Redux/flux, или вы предпочитаете контейнерные компоненты вместо 'async actions';
3. Redux Async Actions
Redux управляет данными, а AJAX предоставляет эти данные с сервера, таким образом целесообразно, что ваш Redux код должен обрабатывать ваши сетевые запросы.
Если вы используете Redux, не помещайте ваш AJAX код в React компоненты. Вместо этого расположите его в Async Actions.
Я рекомендую использовать fetch() чтобы делать сетевые запросы, и к счастью, он же используется в официальной документации Redux. Они даже написали пример reddit API, который использует Redux, React и fetch().
Если вы используете другую реализацию flux, подход аналогичен - отправлять сетевые запросы в ваших действиях.
Когда использовать Redux Async Actions:
- Если вы используете Redux;
- Если вы используете другую реализацию flux, то подход будет аналогичным;
4. Relay
С Relay, вы указываете данные необходимые для ваших React компонентов с помощью GraphQL, а Relay автоматически загружает эти данные и заполняет ими свойства компонента.
Relay отлично работает и подходит для больших приложений, но требует больших временных затрат. Тебе нужно будет:
- Изучить Relay и GraphQL;
- Указать данные необходимые вашим React компонентам с помощью GraphQL (вместо propTypes);
- Настроить GraphQL сервер;
Relay предназначен только для взаимодействия с серверами GraphQL, так что он не сможет взаимодействовать с API сторонних производителей.
В настоящее время Relay может взаимодействовать только с одним сервером GraphQL, так что если вы получаете данные из нескольких источников, этот подход не для вас. Возможность взаимодействовать с несколькими серверами возможно будет добавлена в будущем, это обсуждается на GitHub'е.
Если вы собираетесь использовать этот подход, то Relay Playground прекрасное место где вы сможете узнать о том как работает Relay.
Когда использовать Relay:
- Вы разрабатываете большое приложение, и беспокоитесь о проблемах, которые решает Relay;
- У вас ещё не разработан JSON API;
- Вы готовы настроить GraphQL сервер;
- Ваше приложение будет взаимодействовать только с одним сервером;
Бонус: Анти-паттерны
Если все вышеперечисленные способы правильные, то какие способы не правильный? Вот 2 общих подхода, которые я советую вам избегать.
Анти-паттерн №1: AJAX запросы в презентационных компонентах.
Не добавляйте AJAX логику в компоненты, которые отвечают уже за что-то другое, например за отображение сложного интерфейса. Это нарушит принцип разделения ответственности.
Анти-паттерн №2: ReactDOM.render()
AJAX логика может жить полностью за пределами React и вызвать ReactDOM.render() всякий раз, когда приходят обновления с сервера.
Такой подход может работать нормально, но я перечислил это как анти-паттерн, потому что считаю, что подход с корневым компонентом аналогичен, но чище.
Вывод
Приложения разработанные с React являются модульными. React является всего лишь одним из модулей, а AJAX библиотека другим. Это не Rails или Angular.
Комментарии
В оригинале вывод имеет смысл отличный от того, как вы его перевели. Там "Rеаct является одним из его модулей, а AJAX другим" (т. е. еще одним модулем этого приложения).
Вы правы, поправил. Спасибо за замечание.