在另一个带有反应钩子的 webpack 项目中使用一个 webpack 项目会导致错误

Meb*_*rem 7 reactjs webpack create-react-app react-hooks

create-react-appusing 自定义了三个react-app-rewired,都使用了以下包的相同版本:

"react": "^16.12.0",
"react-app-rewired": "^2.1.5",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.0",
Run Code Online (Sandbox Code Playgroud)

App 1,“主”应用程序是其他两个应用程序的一个非常简单的外壳public/index.html,看起来像这样:

<body>
    <div class="main-app"></div>
    <div class="sub-app-1"></div>
    <div class="sub-app-2"></div>
    <script src="sub-app-1/static/js/bundle.js" async></script>
    <script src="sub-app-1/static/js/0.chunk.js" async></script>
    <script src="sub-app-1/static/js/main.chunk.js" async></script>
    <script src="sub-app-2/static/js/bundle.js" async></script>
    <script src="sub-app-2/static/js/0.chunk.js" async></script>
    <script src="sub-app-2/static/js/main.chunk.js" async></script>
</body>
Run Code Online (Sandbox Code Playgroud)

这工作得很好,所有三个应用程序都正确呈现。现在需求略有变化sub-app-2,例如<PictureFrame />需要将唯一的组件包含在 中sub-app-1,我在主项目中创建了一个存根组件,如下所示:

const NullPictureFrame = () => {
    return <div></div>;
}

export const PictureFrame = (props: Props) => {
    const forceUpdate = useForceUpdate();
    useEventListener(window, "PictureFrameComponent.Initialized" as string, () => forceUpdate());

    const PictureFrame = window.PictureFrameComponent || NullPictureFrame;
    return <PictureFrame />
}
Run Code Online (Sandbox Code Playgroud)

我认为钩子的细节并不重要,但它们在以独立方式运行时确实有效。在sub-app-2做类似这样的东西

window.PictureFrameComponent = () => <PictureFrame />
Run Code Online (Sandbox Code Playgroud)

这在理论上似乎可行,但在实践中我最终遇到以下错误

The above error occurred in the <PictureFrame> component:
    in PictureFrame (at src/index.tsx:17)
    in Router (at src/index.tsx:16)
    in Unknown (at PictureFrames/index.tsx:21)
    in PictureFrame (at src/index.tsx:32) <--- in `main-app`

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries. index.js:1
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.

Run Code Online (Sandbox Code Playgroud)

main-app并且sub-app-2正在加载两个不同版本的react,这在使用钩子时会导致问题。

我曾尝试根据此 github 线程中的建议更新我的 webpack 配置,但似乎sub-app-2找不到react使用此方法的参考。我的main-app/config-overrides.js看起来像这样:

config.resolve.alias["react"] = path.resolve(__dirname, "./node_modules/react");
config.resolve.alias["react-dom"] = path.resolve(__dirname, "./node_modules/react-dom");
Run Code Online (Sandbox Code Playgroud)

sub-app-1/config-overrides.js看起来像这样:

config.externals = config.externals || {};
config.externals.react = {
    root: "React",
    commonjs2: "react",
    commonjs: "react",
    amd: "react"
};
config.externals["react-dom"] = {
    root: "ReactDOM",
    commonjs2: "react-dom",
    commonjs: "react-dom",
    amd: "react-dom"
};
Run Code Online (Sandbox Code Playgroud)

这不会导致任何错误,但也sub-app-2没有被 webpack 初始化为react/的代码react-dom无法找到


编辑1:澄清

  • 有问题的 3 个应用程序是 3 个完全独立的 (git) 项目,只是都使用相同的依赖项。
  • 项目之间唯一允许的“依赖”是通过使用全局变量、事件Window.postMessage或类似的这种非常松散的耦合。
  • 我最初的方法在使用钩子的情况const PictureFrame = window.PictureFrameComponent || NullPictureFrame;工作正常,团队一直使用到现在,这加强了上面详述的非常丢失依赖项的想法
  • 有制作的“明显的”方式“反应”,“反应-DOM”,以及任何有关的部分他们dependendsexternalsmain-app以这种方式和加载它们

Ron*_*nie 2

正如React.js 文档中引用的那样,


反应挂钩:

挂钩本质上是为了访问无类组件中的状态和其他反应功能而构建的。

使用 Hook 时要记住的规则

  1. 仅在顶层调用挂钩

  2. 仅从 React 函数调用 Hook

从您发布的详细信息来看,我相信您已经考虑到了上述几点。

对于根本原因分析,您可能需要在代码中设置一些调试语句或在控制台上记录消息以调查组件生命周期。


另外,如果您可以按如下方式重建/重新排列应用程序,那就更好了:

公共/index.html

<body>
<div id="root" class="main-app"></div>
<script>
<!-- scrpits can be kept here -->
</script>
</body>
Run Code Online (Sandbox Code Playgroud)

创建另一个组件Main App并在其中包含sub-app-1sub-app-2

由于您使用的是"react-dom": "^16.12.0"and "react-router-dom": "^5.1.2",因此您设置路线会更容易。


不能说这真的能解决你的问题,但这是一个很好的做法。

一切顺利。