【问题标题】:React 16 hooks don't work in nested npm packageReact 16 钩子在嵌套的 npm 包中不起作用
【发布时间】:2020-09-14 21:43:55
【问题描述】:

这有点难以解释,但这里是:

我创建了 2 个 npm 包,它们在非 React rails 项目中导入和呈现。我最近做了一些修改,以允许在另一个更现代的 react 项目中使用它,他们正在使用 react 16 和钩子。

第一个包,我们称之为 ComponentA,可以单独使用,也可以嵌套在第二个包中,我们称之为 ComponentB。 ComponentA 和 ComponentB 都是在 react 15.x 中创建的,并且是类而不是使用钩子。

经过一番努力并试图不必更改任何子组件后,我们让这两个包都使用 Context 和 Providers 工作。 ComponentA 独立工作,而 ComponentB 与嵌套在其中的 ComponentA 独立工作。

编辑:

所以经过一些调试后,我认为我有一个理论。它看起来可能是反应的多个版本。我为 ComponentA 和 ComponentB 设置了 webpack 以使用 peerDependencies 并依赖它的父级反应。 (或者至少我认为我做到了)但是我在项目之间遇到了相互矛盾的故事。

当在开发中运行 ComponentB 并在其中嵌套 ComponentA 时,我将 window.React# = React 添加到两者中,然后在 js 控制台中检查了 React1 === React2 并得到了正确的结果。

npm linking ComponentB 进入我的 Rails 应用程序时,我在其中添加了另一个 window.React#,现在所有不同的反应变量都不相等。这也发生在我发布 ComponentA 和 B 并在我的 Rails 应用程序中使用发布的版本时。我彻底糊涂了。

结束编辑

组件A

设置上下文/提供者

import React from "react"

ComponentAContext = React.createContext({})

export class ComponentAProvider extends React.Component
  constructor: (props) ->
    super(props)
    this.state = {...}
   
  get_context: ->
    Object.assign({ ... }, this.state, this.props)

  render: ->
    ctx = this.get_context()
    <ComponentAContext.Provider value={ctx}>
      {this.props.children}
    </ComponentA.Provider>
  
export default ComponentAContext

组件实际使用Context和Provider

import { useContext, useEffect, useRef } from "react"
import ReactDOM from 'react-dom'
import ComponentAContext, { ComponentAProvider } from "./contexts/componentAContext"

ComponentA = (props) =>
  ctx = useContext(ComponentAContext)

  { ... } = ctx

  useEffect () =>
    ...
  , [attr_changed]

  <div>
    Hello World
    {props.children}
  </div>

# just a wrapper for provider and component
ComponentAPackage = (props) =>
  dup_props = Object.assign({}, props)
  children = props.children
  delete dup_props.children

  <ComponentAProvider {...dup_props}>
    <ComponentA>
      {children}
    </ComponentA>
  </ComponentAProvider>

export { ComponentA, ComponentAPackage }

组件B

设置上下文/提供者

import React from "react"

ComponentBContext = React.createContext({})

export class ComponentBProvider extends React.Component
  constructor: (props) ->
    super(props)
    this.state = {}

  render: () ->
    ctx = {
      ...
    }

    <ComponentBContext.Provider value={ Object.assign(ctx, this.state) }>
      {this.props.children}
    </ComponentBContext.Provider>

export default ComponentBContext

组件实际使用Context和Provider

import React, { useContext, useEffect } from "react"
import ReactDOM from 'react-dom'

import { ComponentAPackage } from 'componentA' # npm package
import ComponentBContext, { ComponentBProvider } from "./contexts/componentBContext"

ComponentB = () =>
  ctx = useContext(ComponentBContext)
  { showCompA } = ctx

  useEffect () =>
    ...
  , [changed_attr]

  if showCompA
    <div>
      With nested
      <ComponentAPackage />  
    </div>
  else
    <div>Standalone</div>

class ComponentBPlugin
  constructor: (configs, elm) ->
    compB = <ComponentBProvider {...configs}><ComponentB /></ComponentBProvider>
    ReactDOM.render compB, elm
    this

export { ComponentBPlugin, ComponentB }

我尽量保持简单,但这有点复杂。因此,只需使用 new ComponentBPlugin({...}, elm) 运行 ComponentB 就可以了,但是将其添加到现有的 Ruby on Rails 项目 ComponentA barfs 就可以了,而 ComponentB 很好。如果我将&lt;ComponentA /&gt; 替换为&lt;div /&gt;,则所有内容都会在rails 项目中呈现而不会出现错误。

Uncaught Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

React 位于 ComponentA 和 ComponentB 的 peerDependencies 中,并且位于 Rails 项目的 package.json 中。他们都在期待react@16.13.1

Rails 项目 JS

  { ComponentBPlugin } = require("componentB") # installed npm package
  setup = {...}
  compB = new ComponentBPlugin setup, $(".some-div")[0]

我知道这里发生了很多事情,但我试图让一个遗留项目和一个新的 React 项目使用相同的组件和状态,并试图避免由于时间限制而重写太多并防止已经投入生产的遗留项目可能存在大量错误。

提前致谢

【问题讨论】:

    标签: javascript ruby-on-rails reactjs react-hooks


    【解决方案1】:

    经过大约 2 天的调试,我终于找到了问题所在。这是多个实例化反应的问题。它们都是相同的版本,但被实例化了两次。

    原因

    原来 Rails 应用程序和 ComponentB 共享同一个实例,但嵌套在 ComponentB 中的 ComponentA 有它自己的 react 实例。这是我的理论:ComponentA 是 ComponentB 的依赖项,并且 ComponentA 和 ComponentB 都作为 peerDependency 做出反应,因此两者都在其他地方寻找他们的反应。当 ComponentB 为 npm 编译/捆绑时,webpack 看到它的一个依赖项(ComponentA)需要 react 并继续将 react 与它捆绑在一起。这是当 ComponentB 仍在计划从任何将包含它的内容中获得它的反应时。 (Rails 应用程序)

    TL;博士

    ComponentA 和 React 被捆绑在 ComponentB 中,当 ComponentB 使用 Rails 应用程序中的 react 时,ComponentA 依赖于此。

    解决方案

    解决方案是使 ComponentA 成为 ComponentB 的 peerDependency 以及 Rails 应用程序的 ComponentA 和 ComponentB 依赖项。这样 ComponentA/B 都将寻找 rails 应用程序的反应。

    最后说明

    我觉得应该有一种方法来处理这种情况,就像我最初遇到的那样,并且非常想要一个解决方案,但现在这适用于我的情况,我正在继续前进。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-29
      • 2021-03-25
      • 2021-04-13
      • 1970-01-01
      相关资源
      最近更新 更多