На прошлой неделе я опубликовал рассказ о создании магазина в Электроде @WalmartLabs. Как я уже сказал, помимо этого первого инженерного вопроса, у меня был еще один, на этот раз касающийся рендеринга на стороне сервера и инициализации хранилища, когда требуется предварительная выборка данных.

Проблема

Предположим, у нас есть следующий случай: наше приложение инициализирует определенную страницу, отправляя XHR на наш сервер, получая данные для отображения (настоящий стиль SPA). Однако для рендеринга этой страницы на стороне сервера нам необходимо заранее иметь эти данные. Настоящий вопрос, стоящий за всем этим, заключается в следующем:

Как мы можем инициализировать наше хранилище с помощью таких данных, в то же время обеспечив повторное использование кода настолько, насколько это возможно, и затем передать эти компоненты + данные для рендеринга на стороне сервера? Имейте в виду, что, хотя эту проблему можно решить без особых хлопот при разработке приложения с нуля, я не смог найти простого способа сделать это в приложении Electrode.

В качестве первого шага я решил использовать решение, представленное в моей предыдущей истории, и таким образом попытался найти способ эффективно отправлять действия, выполняющие запросы на выборку на нашем сервере в контексте SSR. .

Примечание. Если вы попытаетесь отправить действия, которые fetch в контексте SSR, ваш сервер либо пожалуется, что предоставленный URL-адрес не является абсолютным и, следовательно, не может двигаться дальше с запросом, либо, если вы указали внешний URL-адрес вашего сервера, просто выполнит классический XHR, чтобы получить эти данные. Разве это не пустая трата ресурсов?

Давайте сделаем шаг назад и посмотрим на наш последний пример предыдущей истории:

В нашем routes.jsx мы определили инициализаторы нашего хранилища. Маршрут /some-route отправляет действие getData для заполнения состояния.

Что, если наша функция getData сделает HTTP-запрос на нашем собственном сервере?

Нет никакого смысла выдавать для них обычные HTTP-запросы при предварительной загрузке для рендеринга на стороне сервера, поскольку они будут обслуживаться одним и тем же процессом! Кроме того, это только сделало бы код более хрупким, потому что нам пришлось бы указывать общедоступные URL-адреса и т. д.

Решение

К счастью, Hapi.js предоставляет нам возможность через свой API получить результат гипотетического запроса, не делая его! Делается это с помощью метода inject.

Если бы только у нас был fetch, который работал бы как обычно в браузере, но вместо этого имитировал бы вызов API в контексте рендеринга на стороне сервера.

Но зачем подвергать такую ​​логику библиотеке fetch, которую загружает клиент, когда контексту клиента никогда не нужно знать особенности SSR?

Может быть, лучше иметь промежуточное программное обеспечение, которое будет снабжать наши действия правильной выборкой, в зависимости от контекста, в котором работает это промежуточное программное обеспечение!

В частности, это промежуточное ПО не должно зависеть от того, в какую библиотеку выборки оно передает действия. Он должен просто передать определенный параметр, который был задан ему при создании. Поэтому, когда хранилище создается на стороне сервера для SSR, используется inject-fetch. Но когда он создается в контексте браузера, должен использоваться обычный fetch!

О, подождите, так что нам просто нужно промежуточное программное обеспечение, которое передает объект, который мы ему даем, в качестве дополнительного параметра для действий преобразователя! Но у нас уже есть одно такое промежуточное ПО!

Это промежуточное ПО redux-thunk!

И подробно, из документов:

Начиная с версии 2.1.0, Redux Thunk поддерживает внедрение пользовательского аргумента с помощью функции withExtraArgument.

Таким образом, мы можем создать наш inject-fetch следующим образом:

Добавьте его в жизненный цикл нашего редукционного магазина следующим образом:

инициализируем его экземпляром нашего сервера HAPI.js:

и используем его в наших действиях так:

Теперь все на месте и SSR работает без сбоев!

Единственный недостаток, который я вижу на данный момент, заключается в том, что действия должны быть структурированы «особым» образом, обращаясь к функции fetch из аргументов действия.

Однако обратите внимание, что эта реализация является просто проверкой концепции, и еще предстоит проделать работу, особенно в inject-fetch для отката к обычному fetch, когда предоставляются URL-адреса, которые не принадлежат нашему серверу!

Ретроспектива

В целом, я нашел предложенное решение относительно чистым, и работать с ним было очень легко! Однако я хотел бы получить ваши отзывы; любые проблемы, которые вы можете увидеть в этом подходе, или, возможно, предложения по его улучшению! Кроме того, если вам известен какой-либо другой способ решить проблему предварительной выборки SSR в Electrode, пожалуйста, прокомментируйте и дайте мне знать!

Я создал два репозитория Github с соответствующим кодом: один для библиотеки inject-fetch ​​(которую я планирую опубликовать на npm) и один с примером приложения, использующего методы, описанные в этой и предыдущей истории. Не стесняйтесь проверять их, сообщать о проблемах или форкнуть!

Удачного кодирования!