1

What i want to do is to increase the speed at which React router loads a component when clicked, right now it takes about 1 second to display it, i want it to show almost instantly once clicked like other less heavy components. However this component has 3 other big components nested inside of it, could that be the reason why it loads slower then usual?

What i want to achieve: - Preload component or premount , prerender it somehow so that when i click on the router link -- it shows the desired big component almost instantly like other less heavy components.

Tried so far: Lazy loading and preloading with React.lazy https://hackernoon.com/lazy-loading-and-preloading-components-in-react-16-6-804de091c82d

unfortunately my app is SSR server side and React.lazy is not supported on server side react apps.

Please advise what should i do to achieve the desired outcome?

Many thanks!!! Ivan

Here is server.js

 /**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import path from 'path';
import express from 'express';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import nodeFetch from 'node-fetch';
import React from 'react';
import ReactDOM from 'react-dom/server';
import PrettyError from 'pretty-error';
import App from './components/App';
import Html from './components/Html';
import { ErrorPageWithoutStyle } from './routes/special/error/ErrorPage';
import errorPageStyle from './routes/special/error/ErrorPage.css';
import createFetch from './createFetch';
import router from './router';
// import assets from './asset-manifest.json'; // eslint-disable-line import/no-unresolved
import chunks from './chunk-manifest.json'; // eslint-disable-line import/no-unresolved
import configureStore from './store/configureStore';
import { setRuntimeVariable } from './actions/runtime';
import config from './config';
const compression = require('compression');

process.on('unhandledRejection', (reason, p) => {
  console.error('Unhandled Rejection at:', p, 'reason:', reason);
  // send entire app down. Process manager will restart it
  process.exit(1);
});

//
// Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the
// user agent is not known.
// -----------------------------------------------------------------------------
global.navigator = global.navigator || {};
global.navigator.userAgent = global.navigator.userAgent || 'all';

const app = express();

//
// If you are using proxy from external machine, you can set TRUST_PROXY env
// Default is to trust proxy headers only from loopback interface.
// -----------------------------------------------------------------------------
app.set('trust proxy', config.trustProxy);

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
app.use(express.static(path.resolve(__dirname, 'public')));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());


//
// Register server-side rendering middleware
// -----------------------------------------------------------------------------
app.get('*', async (req, res, next) => {

/*   if(req.url.includes("sset")) 
  return next(); */

  try {
    const css = new Set();

    // Enables critical path CSS rendering
    // https://github.com/kriasoft/isomorphic-style-loader
    const insertCss = (...styles) => {
      // eslint-disable-next-line no-underscore-dangle
      styles.forEach(style => css.add(style._getCss()));
    };

    // Universal HTTP client
    const fetch = createFetch(nodeFetch, {
      baseUrl: config.api.serverUrl,
      cookie: req.headers.cookie,
    });

    const initialState = {
      user: req.user || null,
    };

    const store = configureStore(initialState, {
      fetch,
      // I should not use `history` on server.. but how I do redirection? follow universal-router
    });

    store.dispatch(
      setRuntimeVariable({
        name: 'initialNow',
        value: Date.now(),
      }),
    );

    // Global (context) variables that can be easily accessed from any React component
    // https://facebook.github.io/react/docs/context.html
    const context = {
      insertCss,
      fetch,
      // The twins below are wild, be careful!
      pathname: req.path,
      query: req.query,
      // You can access redux through react-redux connect
      store,
      storeSubscription: null,
    };

    const route = await router.resolve(context);
    console.log("inside server")

    if (route.redirect) {
       res.redirect(route.status || 302, route.redirect);
      return;
    }

    const data = { ...route };
    data.children = ReactDOM.renderToString(
      <App context={context}>{route.component}</App>,
    );
    data.styles = [{ id: 'css', cssText: [...css].join('') }];

    const scripts = new Set();
    const addChunk = chunk => {
      if (chunks[chunk]) {
        chunks[chunk].forEach(asset => scripts.add(asset));
      } else if (__DEV__) {
        throw new Error(`Chunk with name '${chunk}' cannot be found`);
      }
    };
    addChunk('client');
    if (route.chunk) addChunk(route.chunk);
    if (route.chunks) route.chunks.forEach(addChunk);

    data.scripts = Array.from(scripts);
    data.app = {
      apiUrl: config.api.clientUrl,
      state: context.store.getState(),
    };

    const html = ReactDOM.renderToStaticMarkup(<Html {...data} />);
    res.status(route.status || 200);


    res.send(`<!doctype html>${html}`);
  } catch (err) {
    next(err);
  }
});

//
// Error handling
// -----------------------------------------------------------------------------
const pe = new PrettyError();
pe.skipNodeFiles();
pe.skipPackage('express');

// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
  console.error(pe.render(err));
  const html = ReactDOM.renderToStaticMarkup(
    <Html
      title="Internal Server Error"
      description={err.message}
      styles={[{ id: 'css', cssText: errorPageStyle._getCss() }]} // eslint-disable-line no-underscore-dangle
    >
      {ReactDOM.renderToString(<ErrorPageWithoutStyle error={err} />)}
    </Html>,
  );
  res.status(err.status || 500);
  res.send(`<!doctype html>${html}`);
});

//
// Launch the server
// -----------------------------------------------------------------------------
if (!module.hot) {
  app.listen(config.port, () => {
    console.info(`The server is running at http://localhost:${config.port}/`);
  });
}

//
// Hot Module Replacement
// -----------------------------------------------------------------------------
if (module.hot) {
  app.hot = module.hot;
  module.hot.accept('./router');
}
export default app;

Here is router

/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

/* eslint-disable global-require */

// The top-level (parent) route
const routes = {
  path: '',

  // Keep in mind, routes are evaluated in order
  children: [
     require('./member/invoices/new').default,

    {
      path: '',
      load: () => import(/* webpackChunkName: 'auth' */ './auth/login'),
    },
    {
      path: '/invoices',
      children: [
        {
          path: '',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/invoices'),
        },

        {
          path:
            '/:id' /* 
action: context => `Welcome, ${context.params.id}!` */,
          load: () =>
            import(
              /* webpackChunkName: 'member' */ './member/invoices/preview'
            ),
        },
        {
          path: '/:id/edit',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/invoices/edit'),
        },
      ],
    },

    {
      path: '/quotes',
      children: [
        {
          path: '',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/quotes'),
        },
        {
          path: '/new',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/quotes/new'),
        },
        {
          path:
            '/:id' /* 
action: context => `Welcome, ${context.params.id}!` */,
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/quotes/preview'),
        },
        {
          path: '/:id/edit',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/quotes/edit'),
        },
      ],
    },
    {
      path: '/clients',
      children: [
        {
          path: '',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/clients'),
        },
        {
          path: '/new',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/clients/new'),
        },
        {
          path: '/:id/edit',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/clients/edit'),
        },
      ],
    },
    {
      path: '/items',
      children: [
        {
          path: '',
          load: () => import(/* webpackChunkName: 'member' */ './member/items'),
        },
        {
          path: '/new',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/items/new'),
        },
        {
          path: '/:id/edit',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/items/edit'),
        },
      ],
    },
    {
      path: '/settings',
      load: () => import(/* webpackChunkName: 'member' */ './member/settings'),
    },
    {
      path: '/billing',
      load: () => import(/* webpackChunkName: 'member' */ './member/billing'),
    },
    {
      path: '',
      load: () => import(/* webpackChunkName: 'member' */ './member/dashboard'),
    },

    {
      path: '/subscription',
      children: [
        {
          path: '',
          load: () =>
            import(/* webpackChunkName: 'member' */ './member/subscription'),
        },
      ],
    },

    {
      path: '/auth',
      children: [
        {
          path: '/login',
          load: () => import(/* webpackChunkName: 'auth' */ './auth/login'),
        },
        {
          path: '/login/:token',
          load: () => import(/* webpackChunkName: 'auth' */ './auth/login'),
        },
        {
          path: '/register',
          load: () => import(/* webpackChunkName: 'auth' */ './auth/register'),
        },
        {
          path: '/invoices2',
          load: () =>
            import(/* webpackChunkName: 'auth' */ './member/invoices'),
        },
      ],
    },

    {
      path: '/admin',
      children: [
        {
          path: '',
          load: () => import(/* webpackChunkName: 'admin' */ './admin'),
        },
      ],
    },
    {
      path: '/legal-notice',
      load: () =>
        import(/* webpackChunkName: 'special-page' */ './special/legal'),
    },
    {
      path: '/privacy-policy',
      load: () =>
        import(/* webpackChunkName: 'special-page' */ './special/privacy'),
    },
    {
      path: '/refund-policy',
      load: () =>
        import(/* webpackChunkName: 'special-page' */ './special/refund'),
    },
    {
      path: '/terms-and-conditions',
      load: () =>
        import(/* webpackChunkName: 'special-page' */ './special/terms'),
    },

    // Wildcard routes, e.g. { path: '(.*)', ... } (must go last)
    {
      path: '(.*)',
      load: () =>
        import(/* webpackChunkName: 'not-found' */ './special/not-found'),
    },
  ],

  async action({ next }) {
    // Execute each child route until one of them return the result
    const route = await next();
    console.log(route);

    // Provide default values for title, description etc.
    route.title = `${route.title || 'Untitled Page'} - sAAsTr`;
    route.description = route.description || '';

    return route;
  },
};

// The error page is available by permanent url for development mode
if (__DEV__) {
  routes.children.unshift({
    path: '/error',
    action: require('./special/error').default,
  });
}

export default routes;
  • You can use `loadable-components` to lazy load components in a server-side-rendered application. [server side rendering with loadable-components](https://loadable-components.com/docs/server-side-rendering/) – subashMahapatra May 25 '20 at 20:00
  • if i lazy load them does that mean they will load faster in router? – Ivan Simuria May 25 '20 at 23:15
  • lazy-loading components will allow better code-splitting for your codebase, which will eliminate unnecessary bundle size for other routes. Suppose you have `RouteA` which renders a lazy-loaded component `ComponentA`. If `RouteB` does not render `ComponentA` it will load faster because the code for `ComponentA` will not be bundled for `RouteB`. Can you explain a bit more about your app structure, does it use nextjs or you have your own custom server that server-side-renders the routes? – subashMahapatra May 25 '20 at 23:32
  • it uses express.js server that renders routes server side , please check updated question – Ivan Simuria May 26 '20 at 10:14
  • You should be using [loadable-components](https://loadable-components.com/docs/server-side-rendering/) then. This is also the suggested way in official react-docs. – subashMahapatra May 26 '20 at 18:59
  • but loadable components only support preloading/prerendering on client side which is what i want to do ( can i do that with loadable components? ), code splitting is unfortunately not an option in my project. – Ivan Simuria May 26 '20 at 21:17

1 Answers1

-1

Hı , I can some advices.

  1. Check your api that call in your components. Improve your api methods for faster response.
  2. remove unnecessary uses in your component or improve that.
  3. İf you call database , check your query .
  4. You can debug it backend and find out where you lost time.
  5. For Front-end , improve your components.
Bahtiyar
  • 192
  • 1
  • 6