Search code examples
javascriptreactjswebpackreact-hooksbabeljs

Webpack (in prod) bundles give error: TypeError: (0 , tm.useEffect) is not a function...why?


My React app is using Webpack + Babel. When I compile in development everything works perfectly well.

When I bundle for production ("npm run build") and upload the bundle in prod, an error appear in the console:

Webpack bundle error

Why? I found a similar question but didn't find an answer : related stackoverflow question

Here's my webpack.prod.js config:

const TerserPlugin = require("terser-webpack-plugin");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ProvidePlugin, web } = require("webpack");
const cleanPlugin = require("clean-webpack-plugin");

module.exports = {
  mode: "production",

  devtool: "source-map",
  entry: ["regenerator-runtime/runtime.js", "./src/index.js"],

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index_bundle.js",

  },

  module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        exclude: /[\\/]node_modules[\\/]/,

        use: {
          loader: "babel-loader",
        },
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.svg$/,
        use: {
          loader: "svg-url-loader",
        },
      },
    ],
  },

  resolve: {
    alias: { react: path.resolve("./node_modules/react") },
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "src/index.html",
      title: "Production",
    }),
    //no need to import react in every module
    new ProvidePlugin({
      React: "react",
    }),
    //erase "dist" bundle before every "npm run build"
    new cleanPlugin.CleanWebpackPlugin(),
  ],

  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
      }),
    ],
  },
  
};


Package.json:

{
  "name": "timerfrontend",
  "version": "1.0.0",
  "main": "index.js",
  "babel": {
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
    ]
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack serve --config webpack.config.js",
    "create": "webpack -w",
    "build": "webpack --config webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.16.5",
    "@babel/preset-env": "^7.16.5",
    "@babel/preset-react": "^7.16.5",
    "babel-loader": "^8.2.3",
    "css-loader": "^6.5.1",
    "css-minimizer-webpack-plugin": "^3.3.1",
    "html-webpack-plugin": "^5.3.1",
    "style-loader": "^3.3.1",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.7.1"
  },
  "dependencies": {
    "@apollo/client": "^3.5.6",
    "@apollo/link-context": "^2.0.0-beta.3",
    "@apollo/react-hooks": "^4.0.0",
    "@auth0/auth0-react": "^1.8.0",
    "apollo-cache-inmemory": "^1.6.6",
    "apollo-client": "^2.6.10",
    "apollo-link-http": "^1.5.17",
    "bootstrap": "^5.0.1",
    "clean-webpack-plugin": "^4.0.0",
    "dayjs": "^1.10.5",
    "npm-force-resolutions": "^0.0.10",
    "prop-types": "^15.8.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^6.2.1",
    "regenerator-runtime": "^0.13.9",
    "svg-url-loader": "^7.1.1",
    "terser-webpack-plugin": "^5.3.0",
    "webpack-merge": "^5.8.0"
  },
  "peerDependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "description": "",
  "resolutions": {
    "react": "17.0.2",
    "graphql": "16.1.0"
  }
}

The console error point to that index bundle.js:2 code :

bundle.js code break

The error refer to this react component in my project:

import React, { useEffect } from "react";
import { useMutation } from "@apollo/client";
import { NEW_SESSION, newSessionVariables } from "../../graphql/newSession.js";
import { GET_TOTAL_SESSIONS_TODAY } from "../../graphql/getSessions";
import dayjs from "dayjs";

const sessionCreateDate = dayjs().format("M-D-YYYY");

const TimerAtZero = ({ timerState, toggle, user = { name: "" } }) => {
  const [createSession, { data, loading, error }] = useMutation(NEW_SESSION, {
    refetchQueries: [
      { query: GET_TOTAL_SESSIONS_TODAY, variables: { sessionCreateDate, userName: user.name } },
    ],
  });
  const { endSession } = toggle;

  React.useEffect(() => {
    if (timerState.seconds === 0 && timerState.minutes === 0) {
      endSession();
      createSession(newSessionVariables(user.name, timerState));
    }
  }, [timerState.seconds, timerState.minutes]);

  if (loading) return <div>Submitting...</div>;
  if (error) return <div>{error.message}</div>;
  return null;
};

export default TimerAtZero;



Solution

  • First of all, don't use minified version for finding bugs, it usually shows issues in a weird way that doesn't make sense. Using optimization.minimize = false in webpack can help with that.

    There are cases to look out for when you are using webpack ProvidePlugin.

    The tools that you use for linting, type checking, analyzers, ... should also be configured to detect that global import, which is more work and maintenance in other config files.

    For example for eslint you can do this:

    {
        "globals": {
            "ReactDOM": true
        }
    }
    

    If you choose to change your bundler someday, then you are gonna have a hard time if the other bundler doesn't support something like ProvidePlugin.

    And also new developers might get confused at first if they didn't know about ProvidePlugin. But everyone knows about the import!

    Considering these issues, in the end, it's up to you and your project need to see if it's worth using ProvidePlugin or not.

    If you are using ProvidePlugin only because it's hard to type import react every time, then you need to use snippets in your editor. For example, in VSCode there is a plugin that has this kind of snippets. Or you can create your own snippets if you want.