Начальное состояние Redx-саги на стороне сервера

Я использую React-шаблон для моего приложения (с помощью ветки SSR). По какой-то причине нам нужен рендеринг на стороне сервера. Также у нас есть некоторый API покоя, и нам нужно вызвать один API перед всем API (для регистрации чего-либо). Я думаю, что для начального состояния мне нужно вызвать этот API (первый API, который нуждается в данных для регистрации) на сервере и сохранить данные ответов в хранилище и вернуть хранилище клиенту. В реакторе для создания магазина:

/**
 * Create the store with asynchronously loaded reducers
 */

import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';

const sagaMiddleware = createSagaMiddleware();

export default function configureStore(initialState = {}, history) {
  // Create the store with two middlewares
  // 1. sagaMiddleware: Makes redux-sagas work
  // 2. routerMiddleware: Syncs the location/URL path to the state
  const middlewares = [
    sagaMiddleware,
    routerMiddleware(history),
  ];

  const enhancers = [
    applyMiddleware(...middlewares),
  ];

  // If Redux DevTools Extension is installed use it, otherwise use Redux compose
  /* eslint-disable no-underscore-dangle */
  const composeEnhancers =
    process.env.NODE_ENV !== 'production' &&
    typeof window === 'object' &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
  /* eslint-enable */

  const store = createStore(
    createReducer(),
    fromJS(initialState),
    composeEnhancers(...enhancers)
  );

  // Extensions
  store.runSaga = sagaMiddleware.run;
  store.asyncReducers = {}; // Async reducer registry

  // Make reducers hot reloadable, see http://mxs.is/googmo
  /* istanbul ignore next */
  if (module.hot) {
    module.hot.accept('./reducers', () => {
      import('./reducers').then((reducerModule) => {
        const createReducers = reducerModule.default;
        const nextReducers = createReducers(store.asyncReducers);

        store.replaceReducer(nextReducers);
      });
    });
  }

  return store;
}

а также для определения начального магазина:

  function renderAppToStringAtLocation(url, { webpackDllNames = [], assets, lang }, callback) {
  const memHistory = createMemoryHistory(url);
  const store = createStore({}, memHistory);

  syncHistoryWithStore(memHistory, store);

  const routes = createRoutes(store);

  const sagasDone = monitorSagas(store);

  store.dispatch(changeLocale(lang));

  match({ routes, location: url }, (error, redirectLocation, renderProps) => {
    if (error) {
      callback({ error });
    } else if (redirectLocation) {
      callback({ redirectLocation: redirectLocation.pathname + redirectLocation.search });
    } else if (renderProps) {
      renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames })
    .then((html) => {
      const notFound = is404(renderProps.routes);
      callback({ html, notFound });
    })
    .catch((e) => callback({ error: e }));
    } else {
      callback({ error: new Error('Unknown error') });
    }
  });
}

и для заполнения начального состояния я делаю некоторые изменения:

    async function fetches (hostname) {
  const domain = hostname.replace('.myExample.com', '').replace('www.', '');
  const options = {
    method: 'GET',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/example.api.v1.0+json',
    }
  };
  const uri ='https://api.example.com/x/' + domain + '/details';
  const shopDetail = await fetch(uri, options);
  return shopDetail.json();
}

function renderAppToStringAtLocation(hostname ,url, { webpackDllNames = [], assets, lang }, callback) {
  const memHistory = createMemoryHistory(url);
  console.log('url :', hostname);
  fetches(hostname).then( data => {
    const store = createStore(data, memHistory);

    syncHistoryWithStore(memHistory, store);

    const routes = createRoutes(store);

    const sagasDone = monitorSagas(store);

    store.dispatch(changeLocale(lang));

    match({ routes, location: url }, (error, redirectLocation, renderProps) => {
      if (error) {
    callback({ error });
      } else if (redirectLocation) {
    callback({ redirectLocation: redirectLocation.pathname + redirectLocation.search });
      } else if (renderProps) {
    renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames })
      .then((html) => {
        const notFound = is404(renderProps.routes);
        callback({ html, notFound });
      })
      .catch((e) => callback({ error: e }));
      } else {
    callback({ error: new Error('Unknown error') });
      }
    });
  });

и тогда в консоли я получаю эту ошибку:

 Unexpected properties "code", "data" found in initialState argument 
 passed to createStore. Expected to find one of the known reducer 
 property names instead: "route", "global", "language". Unexpected 
 properties will be ignored.

как это исправить?

1 ответ

Я думаю, для начального состояния мне нужно вызвать этот (первый API, который нуждается в данных для регистрации) API на сервере и сохранить данные ответа в хранилище и вернуть хранилище клиенту

Существует два разных решения, в зависимости от стороны, на которых должен выполняться вызов API.

Если это просто вызов на стороне сервера, ответ HTTP и последующая фаза SSR должны быть отложены до fetch готово. Это может быть решено в express оборачивая в функцию промежуточного программного обеспечения. Обычно такая схема используется при интеграции с внешними сервисами авторизации (Auth0, Passport и т. Д.), Но лучше обернуть информацию об авторизации в JWT а не в INITIAL_STATE,

Если вызов API можно выполнить со стороны клиента, просто используйте redux-saga, Он может порождать выделенный процесс, который перехватит все избыточные действия перед вызовом API, а затем воспроизведет их соответственно. В этом случае initialState объект должен содержать структурные поля без данных, которые будут заполнены позже после вызова API.

Другие вопросы по тегам