In the process of migrating my portfolio from class components to functional components with hooks, I'm getting some errors in my production build that weren't occurring before. All of my components are functional components (except for the error boundary) that use either useState or useEffect, but it looks like the presence of either is causing this error:
"Invariant Violation: Minified React error #298; Hooks can only be called inside the body of a function component."
One of the components causing the error:
import React, { useState, memo } from 'react';
import { NavLink } from 'react-router-dom';
import { NavBrand, Nav } from './styles';
import Hamburger from './Hamburger';
import { Links, Link } from './styles';
function NavBar() {
const [open, handleMenu] = useState(false);
return (
<Nav>
<NavBrand onClick={() => handleMenu(false)}>
<NavLink to="/">
<i className="icon-brand" />
</NavLink>
</NavBrand>
<Hamburger open={open} onClick={() => handleMenu(!open)} />
<Links open={open} role="menu">
<Link
onClick={() => handleMenu(false)}
to="/"
exact
>
Home <i className="icon-home" />
</Link>
<Link
onClick={() => handleMenu(false)}
to="/portfolio"
>
Portfolio <i className="icon-briefcase" />
</Link>
<Link
onClick={() => handleMenu(false)}
to="/contact"
>
Contact <i className="icon-message-square" />
</Link>
</Links>
</Nav>
);
};
export default memo(NavBar);
Webpack.config.js
module.exports = function(env, argv) {
const isProd = argv.mode === 'production';
const plugins = [
new webpack.DefinePlugin({
NODE_ENV: JSON.stringify(argv.mode),
GA_ID: JSON.stringify(process.env.GA_ID)
}),
new WebpackBar({ name: 'portfolio', color: '#269bda' })
];
if (isProd) {
plugins.push(
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
}),
new CopyWebpackPlugin(['netlify', 'pwa', 'static']),
new ManifestPlugin({
fileName: 'asset-manifest.json'
}),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css$/,
minRatio: 0.9,
deleteOriginalAssets: false
}),
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
// staticFileGlobs: ['/vendor.bundle.js'],
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
console.log(message);
},
// minify and uglify the script
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: '/index.html',
// Don't precache sourcemaps, build asset manifest,
// netlify redirects, or app js.
staticFileGlobsIgnorePatterns: [
/\.map$/,
/manifest.json$/,
/_redirects$/,
/js.bundle.js$/,
/[0-9].bundle.js$/
]
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.optimize.AggressiveMergingPlugin(),
new MiniCssExtractPlugin({
filename: 'styles.css',
chunkFilename: '[id].css'
})
);
} else {
plugins.push(
new webpack.HotModuleReplacementPlugin(),
new BrowserSyncPlugin(
// BrowserSync options
{
host: 'localhost',
port: 8080,
open: false,
// proxy the Webpack Dev Server endpoint
// (which should be serving on http://localhost:8080/)
// through BrowserSync
proxy: 'http://localhost:8080/',
logPrefix: 'Portfolio'
},
// prevent BrowserSync from reloading the page
// and let Webpack Dev Server take care of this
{
reload: true
}
),
new CaseSensitivePathsPlugin(),
new FriendlyErrorsWebpackPlugin(),
new SystemBellPlugin(),
new DuplicatePackageCheckerPlugin(),
new StyleLintPlugin({
files: './app/assets/scss/*.scss'
})
);
}
return {
devtool: isProd ? 'hidden-source-map' : 'cheap-module-source-map',
context: sourcePath,
entry: {
js: [
// react-error-overlay
!isProd && 'react-dev-utils/webpackHotDevClient',
// fetch polyfill
isProd && 'whatwg-fetch',
// app entry
'app.tsx'
].filter(Boolean)
},
output: {
path: publicPath,
filename: '[name].bundle.js',
devtoolModuleFilenameTemplate: isProd
? info => path.relative(sourcePath, info.absoluteResourcePath).replace(/\\/g, '/')
: info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')
},
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
use: {
loader: 'html-loader'
}
},
{
test: /\.js$/,
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: false
}
},
{
test: /\.json$/,
loader: 'json-loader',
type: 'javascript/auto'
},
{
test: /\.(scss|css)$/,
use: [
isProd && {
loader: MiniCssExtractPlugin.loader
},
!isProd && {
loader: 'style-loader',
options: {
sourceMap: false
}
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
].filter(Boolean)
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.(ts|tsx)?$/,
use: 'ts-loader',
exclude: /node_modules/
},
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' },
{
test: /\.(ttf|eot|svg|woff|woff2)(\?[a-z0-9]+)?$/,
loader: 'file-loader'
},
{
test: /\.(png|jpg)$/,
exclude: /node_modules/,
use: ['file-loader']
}
]
},
resolve: {
extensions: [
'.webpack-loader.js',
'.web-loader.js',
'.loader.js',
'.jsx',
'.tsx',
'.js',
'.ts'
],
modules: [path.resolve(__dirname, 'node_modules'), sourcePath]
},
plugins,
// split out vendor js into its own bundle
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial'
}
}
}
},
performance: isProd && {
maxAssetSize: 600000,
maxEntrypointSize: 600000,
hints: 'warning'
},
stats: {
colors: {
green: '\u001b[32m'
}
},
devServer: {
contentBase: './src',
historyApiFallback: true,
port: 8080,
compress: isProd,
inline: !isProd,
hot: false,
quiet: true,
before: function(app) {
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
}
}
};
};
The error doesn't occur in my development environment and I've added the react-hooks eslint plugin which should catch any of these invariant violations.
Has anyone else encountered prod-specific issues with React 16.7.0-alpha like this? What am I doing wrong?