0

I'm using react-boilerplate for my App (Using SSR branch). For some reason we need server side rendering. Also we have some rest API and we need to call one API before all API (for register something). I thinks for initial state I need to call this (first API that need data for registration ) API on the server and save response data into store and return store to client. In react-boilerplate for create store:

/**
 * 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;
}

and also for making initial store defined :

  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') });
    }
  });
}

and for filling the initial state I do some change:

    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') });
      }
    });
  });

and then in console I get this 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.

how to fix it?

Saeed Jalali
  • 416
  • 4
  • 16

1 Answers1

0

I thinks for initial state I need to call this (first API that need data for registration ) API on the server and save response data into store and return store to client

There are two different solutions, dependent on side, on which API call should be performed.

If it's just server-side call, HTTP response and subsequent SSR phase should be delayed, until fetch is done. It can be solved in express by wrapping into middleware function. Usually such schema is used when integrating with external authorization services (Auth0, Passport, etc), but it's better to wrap authorization information into JWT and not into INITIAL_STATE.

If API call can be done from client side, just use redux-saga. It can spawn dedicated process, which will catch all redux actions before API call is done, and then play them respectively. In this case initialState object should contain structure-like fields without data, which will be filled later after API call.

Vladislav Ihost
  • 2,127
  • 11
  • 26