Search code examples
javascriptreactjswebpackmicro-frontendwebpack-module-federation

Module federation plugin SSR throws error "Cannot read properties of null (reading 'useState')"


I'm try to Connect my federated module from server side with React.lazy. My same issue in @module-federation/node repo (https://github.com/module-federation/universe/issues/493)

I have custom React ssr server. (https://github.com/ignatiqq/react-ssr-blog/blob/master/src/server/modules/render/render.tsx)

I'm import it like this:

const Homepage = React.lazy(() => import('homePage/Homepage'));

// router code

{path: '/overview', element: <Homepage />},

On client all works ok. But on server i have problems with the above error.

enter image description here

deps:

"@module-federation/node": "^0.9.11",
"react": "^18.2.0",
"webpack": "^5.74.0",

shell app webpack config (client):

 {
    name: 'client',
    target: 'web',
    entry: {
        client: path.resolve(__dirname, '../../src/client/index.ts'),
    },
    output: {
        path: path.resolve(__dirname, '../../dist/client'),
        filename: 'js/[name].[contenthash].js',
        publicPath: '/static/',
        clean: true,
        assetModuleFilename: 'images/[hash][ext]',
    },
    optimization: {
        moduleIds: 'deterministic',
        minimize: !isDev ? true : false,
        minimizer: !isDev ? [new TerserPlugin()] : [],
    },
    module: {
        rules: [
            ...rules,
            {
                test: /\.s[ac]ss$/i,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
    plugins: [
        ...plugins.client,
    ],
};

Webpack plugin part of shell app:

const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const {NodeFederationPlugin, StreamingTargetPlugin} = require('@module-federation/node');

const path = require('path');

const dependencies = require(path.join(__dirname, '../../../package.json')).dependencies;

const remotes = {
    server: {
        homePage: 'homePage@http://localhost:8080/server/homePageRemote.js',
    },
    client: {
        homePage: 'homePage@http://localhost:8080/client/homePageRemote.js',
    },
};

module.exports = {
    client: [
        new ModuleFederationPlugin({
            name: 'shellApp',
            remotes: {...remotes.client},
            shared: {
                react: {
                    singleton: true,
                    requiredVersion: dependencies['react'],
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: dependencies['react-dom'],
                },
            },
        }),
    ],
    server: [
        new NodeFederationPlugin({
            name: 'shellApp',
            library: {type: 'commonjs-module'},
            remotes: {...remotes.server},
            shared: {
                react: {
                    singleton: true,
                    requiredVersion: dependencies['react'],
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: dependencies['react-dom'],
                },
            },
        }),
        new StreamingTargetPlugin({
            name: 'shellApp',
            library: { type: 'commonjs-module' },
            remotes: {...remotes.server},
        }),
    ],
};

Webpack part of remote app: https://github.com/ignatiqq/hp-microfront-for-ssr-blog/blob/master/webpack/server/webpack.server.js

const path = require('path');
const mainRules = require('../additionally/rules');
const { NodeFederationPlugin, StreamingTargetPlugin } = require('@module-federation/node');
const extensions = require('../additionally/extensions');

const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production';
const isDev = mode === 'development';

const dependencies = require('../../package.json').dependencies;

module.exports = {
    entry: {
        server: path.join(__dirname, '../../src/index.tsx')
    },
    stats: {
        colors: true,
        entrypoints: false,
        children: false,
    },
    target: false,
    output: {
        path: path.join(__dirname, '../../dist/server'),
        filename: '[name].[contenthash].js',
        clean: true,
        publicPath: 'http://localhost:8080/server',
    },
    resolve: {
        extensions,
    },
    devtool: isDev ? 'inline-source-map' : 'source-map',
    module: {
        rules: [
            ...mainRules,
            {
                test: /\.(css|scss)$/i,
                    use: [
                    {
                        loader: "css-loader",
                    },
                    {
                        loader: "sass-loader",
                    },
                ],
                exclude: /node_modules/
            }
        ],
    },
    plugins: [
        new NodeFederationPlugin({
            name: 'homePage',
            filename: 'homePageRemote.js',
            library: { type: "commonjs-module" },
            remotes: {},
            exposes: {
                './Homepage': path.resolve(__dirname, '../../src/Homepage.tsx'),
            },
            shared: {
                react: {
                    singleton: true,
                    requiredVersion: dependencies['react'],
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: dependencies['react-dom'],
                }
            }
        }),
        new StreamingTargetPlugin({
            name: 'homePage',
            library: { type: "commonjs-module" },
            remotes: {},
            shared: {
                react: {
                    singleton: true,
                    requiredVersion: dependencies['react'],
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: dependencies['react-dom'],
                }
            }
        })
    ]
}

Can someone help me please? Why dependencies are not sharing?

UPD: UniversalFederationPlugin throws same error


Solution

  • For me solution was adding node-fetch package, that is required by mf remoteEntry build