gregberge / loadable-components

The recommended Code Splitting library for React ✂️✨
https://loadable-components.com
MIT License
7.69k stars 382 forks source link

Hydrate error (Warning: Did not expect server HTML to contain a [ ] in [ ].) #409

Closed coloudman closed 5 years ago

coloudman commented 5 years ago

I use loadable-components with webpack(to compile client, server).

part of server renderer:

  const context = {}; //Router context

  const extractor = new ChunkExtractor({statsFile: statsFilePath, entrypoints: ["index"]});
  const jsx = extractor.collectChunks(
    <StaticRouter location={req.url} context={context}>
      <App/>
    </StaticRouter>
  );
  const body = ReactDOMServer.renderToString(jsx);

  const scriptTags = extractor.getScriptTags();
  const linkTags = extractor.getLinkTags();
  const styleTags = extractor.getStyleTags();

and client code:

import App from "./App.js";

import React from "react";
import ReactDOM from "react-dom";

import {BrowserRouter, Router} from "react-router-dom";
import { loadableReady, loadComponents } from '@loadable/component'

loadableReady(() => {
  const root = document.getElementById('view')
  ReactDOM.hydrate(
    <BrowserRouter>
      <App/>
    </BrowserRouter>
  , root)
});

ReactDOM.hydrate is working like ReactDOM.render(redraw) and throw warning: Warning: Did not expect server HTML to contain a \<div> in \<div>.

Everything work except that warning.

how can i fix it?

hq229075284 commented 5 years ago

if add scriptTags to html body,and if use @loadable/webpack-plugin in client side?

hq229075284 commented 5 years ago
const path = require("path");
const webpack = require("webpack");
const htmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { NODE_ENV } = process.env;
const LoadablePlugin = require('@loadable/webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isHMR = NODE_ENV !== 'production'

module.exports = {
  mode: "development",
  entry: {
    index: [path.join(__dirname, "./src/entry-client.js")]
  },
  output: {
    path: path.join(__dirname, "./dist/client"),
    filename: "[name].js",
    publicPath: '/client/'
  },
  module: {
    rules: [
      { test: /\.js$/, loader: "babel-loader", exclude: /node_modules/ },
      { test: /\.css$/, loader: "css-loader" },
      {
        test: /\.(scss|sass)$/, use: [
          { loader: MiniCssExtractPlugin.loader, options: { hmr: isHMR, reloadAll: true } },
          "css-loader",
          "sass-loader"
        ]
      }
    ]
  },
  devtool: "hidden-source-map",
  plugins: [
    new CleanWebpackPlugin({
      verbose: true
    }),
    new htmlWebpackPlugin({
      template: path.join(__dirname, "./index.html"),
      inject: false,
      filename: '../index.html'
    }),
    new LoadablePlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: 'chunk.[name].css',
    }),
  ]
};

This is my webpack config of client side and work right for me

coloudman commented 5 years ago

I already use scriptTags on SSR and @loadable/webpack-plugin in client side. If use LoadablePlugin() on client, how server load loadable-stats.json?

my client webpack

const isDevelopment = process.env.NODE_ENV === 'development';

const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const LoadablePlugin = require('@loadable/webpack-plugin')

module.exports = [
  {
    entry: {
      index: path.resolve(__dirname, "../src/App/index.js")
    },
    output: {
      publicPath: "/code/",
      path: path.resolve(__dirname, "../assets/static/code/"),
      filename: '[hash:10][id].js',
      chunkFilename: '[hash:10][id].js'
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              babelrc: false,
              presets: [
                "@babel/preset-env", "@babel/preset-react"
              ],
              plugins: ["module:@babel/plugin-proposal-class-properties"]
            }
          }
        }, {
          test: /\.s(a|c)ss$/,
          loader: [
            isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
            {
              loader: 'css-loader',
              options: {
                modules: {
                  localIdentName: '[hash:10]'
                },
                sourceMap: isDevelopment
              }
            }, {
              loader: 'sass-loader'
            }
          ]
        }, {
          test: /\.(png|jpe?g|gif)$/i,
          use: [
            {
              loader: 'file-loader',
              options:{
                publicPath:"/code/"
              }
            }
          ]
        }

      ]
    },
    resolve: {
      extensions: ['.js', '.jsx', '.scss']
    },
    optimization: {
      minimizer: [new UglifyJsPlugin({
          uglifyOptions: {
            mangle: true
          }
        })]
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: '[hash:10][id].css', 
        chunkFilename: '[hash:10][id].css' 
      }),
      new LoadablePlugin({
        writeToDisk: {
          filename: path.resolve(__dirname)
        },
        outputAsset: false
      })
    ],
    mode: isDevelopment
      ? "development"
      : "production"
  }
];

and I don't use @loadable/webpack-plugin in server side.. is it wrong?

hq229075284 commented 5 years ago

Actually,I think @loadable/webpack-plugin is used for client side,because the webpack output assets in server side is not same as client side in sometime due to their webpack config is different.@loadable/webpack-plugin is used to create dependent map,this is use for ChunkExtractor of @loadable/server to output getScriptTags,see more detail,I hope it helped

coloudman commented 5 years ago

I fixed this error! I didn't use LoadableBabelPlugin(@loadable/babel-plugin) in client side webpack config.

Thank you, hq229075284. Your project helped me.