Реакция хуков, генерирующих инвариантные нарушения только в производственной сборке - PullRequest
0 голосов
/ 05 января 2019

В процессе переноса моего портфолио из компонентов класса в функциональные компоненты с перехватчиками я получаю некоторые ошибки в моей производственной сборке, которых раньше не было. Все мои компоненты являются функциональными компонентами (за исключением границы ошибки), которые используют либо useState, либо useEffect, но, похоже, наличие одного из них вызывает эту ошибку:

"Нарушение инварианта: ошибка Minified React # 298; Хуки могут вызываться только внутри тела компонента функции."

Один из компонентов, вызывающих ошибку:

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());
            }
        }
    };
};

Ошибка не возникает в моей среде разработки, и я добавил плагинact-hooks eslint , который должен отлавливать любое из этих инвариантных нарушений.

Кто-нибудь еще сталкивался с проблемами, связанными с продуктом, с React 16.7.0-alpha, как это? Что я делаю не так?

1 Ответ

0 голосов
/ 16 марта 2019

Была проблема с загрузкой двух копий React. Я не знал, что html-webpack-plugin уже добавляет свои скрипты в index.html, поэтому загружалось два набора пакетов.

Как только я удалил скрипты из шаблона, который я передавал html-webpack-plugin, все заработало.

https://reactjs.org/warnings/invalid-hook-call-warning.html

...