Javascript React钩子仅在生产构建中抛出不变冲突

Javascript React钩子仅在生产构建中抛出不变冲突,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,在将我的投资组合从类组件迁移到带有钩子的功能组件的过程中,我在生产构建中遇到了一些以前没有出现过的错误。我的所有组件都是使用useState或useEffect的功能组件(错误边界除外),但这两个组件的存在似乎都会导致此错误: “不变冲突:缩小的反应错误#298;挂钩只能在函数组件主体内部调用。” 导致错误的组件之一: import React, { useState, memo } from 'react'; import { NavLink } from 'react-router-dom'

在将我的投资组合从类组件迁移到带有钩子的功能组件的过程中,我在生产构建中遇到了一些以前没有出现过的错误。我的所有组件都是使用useState或useEffect的功能组件(错误边界除外),但这两个组件的存在似乎都会导致此错误:

“不变冲突:缩小的反应错误#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);
这个错误在我的开发环境中没有发生,我添加了一个可以捕获任何这些不变冲突的


是否有其他人遇到过类似React 16.7.0-alpha的产品特定问题?我做错了什么?

加载两份React是个问题。我没有意识到
html网页包插件
已经在将我的脚本添加到
index.html
,因此有两组包正在加载

一旦我从传递给
html网页插件的模板中删除了脚本,它就工作了


除非您发布使用钩子的代码,否则无法精确定位。我以前遇到过这种情况,在我的案例中,这是因为我将一个函数组件指定给一个常量,然后在另一个函数组件的渲染中使用该常量。这个额外的间接层要么不正确,要么触发了transpiler的bug。当我用
useRef()
定义一个未使用的
const
时,我也遇到了同样的问题。当我移除这条生产线时,生产构建工作正常。webpack似乎对这个未使用的变量进行了一些“聪明”的优化。我会先尝试升级到React 16.8.x,看看这是否是alpha版本的问题。你能粘贴你的package.json吗?
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());
            }
        }
    };
};