Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loadable-components issues with Asp.Net core SPA (JavascriptServices) #29

Closed
BlackFenix2 opened this issue Jan 14, 2018 · 4 comments
Closed

Comments

@BlackFenix2
Copy link

I am attempting to configure Server-side rendering for my Asp.Net core SPA application. the method getLoadableState does not wait for the async components to finish loading. I always get the loading component rendered:

const config = {
  LoadingComponent: () => <Loading />
};

export const Landing = loadable(() => import('../components/Landing'), {
  ...config
});

if i do this for each async component, my SSR works:

import * as Routes from './App/routes';
Routes.Graph.load();
const app = (
        <Provider store={store}>
          <StaticRouter
            basename={basename}
            context={routerContext}
            location={params.location.path}
          >
            <div>
              <Head />
              {/* <Body /> */}
              <Routes.Graph />
              <Foot />
            </div>
          </StaticRouter>
        </Provider>
      );
      params.domainTasks.then(() => {
        getLoadableState(app).then(loadableState => {
          resolve({
            html: renderToString(app).concat(loadableState.getScriptTag()),
            globals: { initialReduxState: store }
          });
        });
      }, reject); // Also propagate any errors back into the host application

i have tried the followind posts, but i am still getting issues with Async SSR:
https://marmelab.com/blog/2017/10/17/code-splitting.html
https://medium.com/smooth-code/introducing-loadable-components-%EF%B8%8F-646dd3ab0aa6 (sorry, your intro blogpost)

i have the full server code below, this is being called by an 'asp-prerender-module' taghelper on .Net Core. I am working off the ReactRedux SPA Template without Typescript.

/* eslint-disable no-console */
import 'babel-polyfill';

import * as React from 'react';
import { getLoadableState } from 'loadable-components/server';
import { renderToString } from 'react-dom/server';

import { Provider } from 'react-redux';

import { StaticRouter } from 'react-router-dom';
// import { replace } from 'react-router-redux';
// import { createMemoryHistory } from 'history';
import { createServerRenderer } from 'aspnet-prerendering';

import configureStore from './state/store/configureStore';

import Body from './App/Body';
import Head from './App/Header';
import Foot from './App/Footer';
// import * as Routes from './App/routes';

export default createServerRenderer(
  params =>
    new Promise((resolve, reject) => {
      // Prepare Redux store with in-memory history, and dispatch a navigation event
      // corresponding to the incoming URL

      const basename = params.baseUrl.substring(0, params.baseUrl.length - 1); // Remove trailing slash
      // const urlAfterBasename = params.url.substring(basename.length);
      // const store = configureStore(createMemoryHistory());

      const store = configureStore;
      // store.dispatch(replace(urlAfterBasename));

      // Prepare an instance of the application and perform an inital render that will
      // cause any async tasks (e.g., data access) to begin
      const routerContext = {};

      // Routes.Graph.load();
      const app = (
        <Provider store={store}>
          <StaticRouter
            basename={basename}
            context={routerContext}
            location={params.location.path}
          >
            <div>
              <Head />
              <Body />
              {/* <Routes.Graph /> */}
              <Foot />
            </div>
          </StaticRouter>
        </Provider>
      );
      renderToString(app);

      // If there's a redirection, just send this information back to the host application
      if (routerContext.url) {
        resolve({ redirectUrl: routerContext.url });
      }

      // Once any async tasks are done, we can perform the final render
      // We also send the redux store state, so the client can continue execution where the server left off
      params.domainTasks.then(() => {
        getLoadableState(app).then(loadableState => {
          resolve({
            html: renderToString(app).concat(loadableState.getScriptTag()),
            globals: { initialReduxState: store }
          });
        });
      }, reject); // Also propagate any errors back into the host application
    })
);

@BlackFenix2
Copy link
Author

to comment, i might be doing something wrong with my getLoadableState method.

import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { getLoadableState } from 'loadable-components/server'
import App from './App'

const app = (
  <StaticRouter>
    <App />
  </StaticRouter>
)

// Traversing React tree to load all modules
getLoadableState(app).then(() => {
  const html = renderToString(<YourApp />) // what is "<YourApp /> in this context? could be what i am missing.
})

@gregberge
Copy link
Owner

It is difficult to help you with this portion of code, but I will try.

Is getLoadableState(app) resolved? Is the then called? If yes your setup is right.

The problem could be that a loadable-components is not present in the React Tree, so it is undetectable for getLoadableState(app) do you see what I mean?

@BlackFenix2
Copy link
Author

NVM, i figured out the issue. i forgot to declare external on my webpack server config:

// /
const nodeExternals = require('webpack-node-externals');
const path = require('path');
const webpack = require('webpack');

const ExtractTextPlugin = require('extract-text-webpack-plugin');

const outputPath = path.resolve('wwwroot', 'server');

const config = {
  // node specific settings
  // target: 'node',
  externals: nodeExternals(), // <- this is what i forgot
  // find starting js file
  entry: {
    server: './ClientApp/server.jsx'
    // serverTest: './ClientApp/serverTest.jsx'
  },
  // add file extensions to shorthand ES6 imports
  resolve: {
    extensions: ['json', '.js', '.jsx', '.css']
  },
  // bundled code output
  output: {
    path: path.resolve(__dirname, outputPath),
    publicPath: '/',
    filename: '[name].js',
    libraryTarget: 'commonjs2'
  },

  module: {
    loaders: [
      // Check JS and JSX files before building
      {
        enforce: 'pre',
        test: /\.jsx?$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      },
      {
        // check each loader for matching files
        oneOf: [
          // default loader for js and jsx with inline babel config
          {
            test: /\.jsx?$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
            options: {
              presets: ['env', 'react'],
              plugins: [
                'babel-plugin-transform-runtime',
                'babel-plugin-dynamic-import-node',
                'babel-plugin-transform-object-rest-spread',
                'babel-plugin-transform-class-properties',
                'transform-es2015-modules-commonjs'
              ],
              // Don't read from .babelrc
              babelrc: false
            }
          },
          // default css loader
          // {
          //   test: /\.css$/,
          //   loader: ExtractTextPlugin.extract({
          //     use: 'css-loader',
          //     fallback: 'style-loader'
          //   })
          // },

          // fallback loader if other loaders excluded
          // URL loader falls back to file-loader
          {
            loader: 'url-loader',

            exclude: [/\.(js|jsx|mjs|css)$/, /\.html$/, /\.json$/],
            options: {
              name: '[name].[ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    // load css into separate .css file
    // new ExtractTextPlugin({
    //   filename: 'server.css',
    //   allChunks: true
    // })
  ]
};

module.exports = config;

now my Async Server-side rendering works perfectly now! sorry to bother!

@gregberge
Copy link
Owner

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants