0

I am kind of new to react with a few months experience, I am working on this project where I need to implement dynamic routes. For that I am using this route.js file.

// These are the pages you can go to.
// They are all wrapped in the App component, which should contain the navbar etc
// See http://blog.mxstbr.com/2016/01/react-apps-with-pages for more information
// about the code splitting business
import { getAsyncInjectors } from 'utils/asyncInjectors';
import App from 'containers/App';

const errorLoading = (err) => {
  console.error('Dynamic page loading failed', err); // eslint-disable-line no-console
};

const loadModule = (cb) => (componentModule) => {
  cb(null, componentModule.default);
};

function createChildRoutes(store) {
  // create reusable async injectors using getAsyncInjectors factory
  const { injectReducer, injectSagas } = getAsyncInjectors(store); // eslint-disable-line no-unused-vars
  let previousPath = null;
  return [
    {
      path: '/',
      getComponent(nextState, cb) {
        if (nextState.location.pathname === previousPath) {
          return;
        }
        const importModules = Promise.all([
          import('containers/HomePage/reducer'),
          import('containers/App/sagas'),
          import('containers/HomePage/sagas'),
          import('containers/HomePage'),
        ]);

        const renderRoute = loadModule(cb);

        importModules.then(([reducer, appSagas, sagas, component]) => {
          injectReducer('homePage', reducer.default);
          injectSagas([...appSagas.default, ...sagas.default]);

          renderRoute(component);
        });

        importModules.catch(errorLoading);
        previousPath = nextState.location.pathname;
      },
    },
    {
      path: '/:urlKey',
      getComponent(nextState, cb) {
        if (nextState.location.pathname === previousPath) {
          return;
        }
        const importModules = Promise.all([
          import('containers/CatalogPage/actions'),
          import('containers/CatalogPage/reducer'),
          import('containers/App/sagas'),
          import('containers/CatalogPage/sagas'),
          import('containers/CatalogPage'),
        ]);

        const renderRoute = loadModule(cb);

        importModules.then(([actions, reducer, appSagas, sagas, component]) => {
          injectReducer('catalogPage', reducer.default);
          injectSagas([...appSagas.default, ...sagas.default]);
          renderRoute(component);
          store.dispatch(actions.loadPageData(nextState.params.urlKey));
        });

        importModules.catch(errorLoading);
        previousPath = nextState.location.pathname;
      },
    }, {
      path: '*',
      getComponent(nextState, cb) {
        import('containers/NotFoundPage')
          .then(loadModule(cb))
          .catch(errorLoading);
      },
    },
  ];
}

// Set up the router, wrapping all Routes in the App component
export default function createRootRoute(store) {
  return {
    component: App,
    childRoutes: createChildRoutes(store),
  };
}

On path: '/:urlKey'this component is imported and By using this.props.loadPageData(this.props.params.urlKey); I dispatch an action in componentWillMount() to enable ssr for the route. For enabling routing from the catalog component I was required to dispatch an action from routes.js too.

My component file coatalog.js

/* eslint no-tabs: "off", no-trailing-spaces: "off", indent: "off", react/jsx-indent: "off", react/jsx-indent-props: "off"  */
/*
 *
 * CatalogPage - this will handle conditions for page type and route the data to their specific components.
 *
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Helmet } from 'react-helmet';
import { createStructuredSelector } from 'reselect';

// import PageWrapperContainer from 'components/PageWrapperContainer';
import CategoryPages from 'components/CategoryPages';
import ProductDetailsPages from 'components/ProductDetailsPages';

import { deviceTypeSelect, isMobileSelect, atcLoaderStateSelect } from 'containers/App/selectors';
import { addToCart } from 'containers/App/actions';

import { loadPageData, getCurrentCategoryState } from './actions';

import { pageDataSelect, 
        currentCategoryStateSelect, 
        metaTitleSelect,
        metaDescriptionSelect,
        pageTypeSelect,
        contentTypeSelect,
        pageHeadingSelect,
        pageSubheadingSelect,
        selectAppLoading } from './selectors';

export class CatalogPage extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
    componentWillMount() {
    // dispatching the urlKey 
        this.props.loadPageData(this.props.params.urlKey);
    }

    render() {
        const { metaTitle,
                metaDescription,
                pageType,
                contentType,
                categoryPageHeading,
                categoryPageSubHeading,
                pageData,
                appLoading } = this.props;
        // console.log(this.props);
        return (
            <div>
                <Helmet>
                    <title>{metaTitle}</title>
                    <meta name="description" content={metaDescription} />
                </Helmet>

                {(appLoading !== false) ? <p>Loading.....</p> : null}

                { ((appLoading !== true) && (pageType === 'category')) ? 
                    <CategoryPages
                        pageData={pageData}
                        contentType={contentType}
                        categoryPageHeading={categoryPageHeading}
                        categoryPageSubHeading={categoryPageSubHeading}
                        currentCategoryState={this.props.currentCategoryState}
                        getCurrentCategoryState={this.props.getCurrentCategoryState}
                        deviceTypeSelect={this.props.deviceTypeSelect}
                        isMobileSelect={this.props.isMobileSelect}
                        atcLoaderStateSelect={this.props.atcLoaderStateSelect}
                        addToCart={this.props.addToCart}
                    />
                    : null
                }
                { ((appLoading !== true) && (pageType === 'product')) ? 
                    <ProductDetailsPages
                        pageData={pageData}
                        contentType={contentType}
                        deviceTypeSelect={this.props.deviceTypeSelect}
                        isMobileSelect={this.props.isMobileSelect}
                        atcLoaderStateSelect={this.props.atcLoaderStateSelect}
                        addToCart={this.props.addToCart}
                    />
                    : null
                }

            </div>
        );
    }
}

CatalogPage.propTypes = {
    params: PropTypes.object,
    urlKey: PropTypes.string,
    loadPageData: PropTypes.func,
    pageData: PropTypes.oneOfType([
        PropTypes.object,
        PropTypes.any,
    ]),
    currentCategoryState: PropTypes.string,
    getCurrentCategoryState: PropTypes.func,
    metaTitle: PropTypes.string,
    metaDescription: PropTypes.string,
    pageType: PropTypes.string,
    contentType: PropTypes.string,
    categoryPageHeading: PropTypes.string,
    categoryPageSubHeading: PropTypes.string,
    appLoading: PropTypes.bool,
    deviceTypeSelect: PropTypes.string,
    isMobileSelect: PropTypes.bool,
    atcLoaderStateSelect: PropTypes.any,
    addToCart: PropTypes.func,
};

const mapStateToProps = createStructuredSelector({
    pageData: pageDataSelect(),
    currentCategoryState: currentCategoryStateSelect(),
    metaTitle: metaTitleSelect(),
    metaDescription: metaDescriptionSelect(),
    pageType: pageTypeSelect(),
    contentType: contentTypeSelect(),
    categoryPageHeading: pageHeadingSelect(),
    categoryPageSubHeading: pageSubheadingSelect(),
    appLoading: selectAppLoading(),
    deviceTypeSelect: deviceTypeSelect(),
    isMobileSelect: isMobileSelect(),
    atcLoaderStateSelect: atcLoaderStateSelect(),
});

function mapDispatchToProps(dispatch) {
    return {
        loadPageData: bindActionCreators(loadPageData, dispatch),
        getCurrentCategoryState: bindActionCreators(getCurrentCategoryState, dispatch),
        addToCart: bindActionCreators(addToCart, dispatch),
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(CatalogPage);

My Problem with this is that On the first render of the path localhost:3000/:urlKey the component is rending twice since I am dispatching the action twice i.e in componentWillMount() and routes.js. I need help to improve this code to stop re-rendering of the component by blocking the dispatch of the action from routes.js on first load of app.

Kunal arora
  • 167
  • 1
  • 2
  • 8

1 Answers1

0

If you want something to not happen on the server and only on the client, you can do it in componentDidMount instead of componentWillMount.

componentWillMount is executed on both the server and the client. componentDidMount only on the client.

So in your case, you can run do your dispatch in the routes.js on the server and in componentDidMount on the client.