React-leaflet 自定义组件 - 未传递上下文?

Posted

技术标签:

【中文标题】React-leaflet 自定义组件 - 未传递上下文?【英文标题】:React-leaflet custom component - context not being passed? 【发布时间】:2020-04-25 17:41:31 【问题描述】:

我正在为 react-leaflet 编写一个自定义组件。这是一个可编辑的弹出窗口,还有一些其他功能。以Here is a codesandbox 为例。该组件的源代码是here。这个例子只是做了一个import EditablePopup from 'my-react-leaflet-popup-plugin',这是我项目中的一个.js文件。效果很好。

我正在尝试使用 webpack 将其打包为节点模块,以便其他人可以使用它。它编译没有问题。你可以看到我的 webpack.config.js here。然后我使用npm link 将此模块链接到我本地机器上的测试项目中。当我这样做时,我收到错误消息:

TypeError: Cannot read property 'leafletElement' of null

  460 |   value: function value() 
  461 |     var e = this;
  462 |     this.props.open && setTimeout(function () 
> 463 |       e.thePopup.leafletElement._source.openPopup();
      | ^  464 |     , .001);
  465 |   
  466 | , 

即使我去掉那个子句,我也会得到这个:

TypeError: Cannot read property 'openPopup' of undefined


  9226 | // @method openOn(map: Map): this
  9227 | // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
  9228 | openOn: function openOn(map) 
> 9229 |   map.openPopup(this);
       | ^  9230 |   return this;
  9231 | ,
  9232 | onAdd: function onAdd(map) 

就像弹出窗口试图将自己添加到地图,但没有找到父地图组件的地图实例。我的预感是,由于某种原因,当使用 webpack 构建它并将其作为 npm 模块导入时,react-leaflet 映射的上下文没有正确传递给我的组件。我不太明白,因为我的组件只是 react-leaflet 的 <Popup> 组件的修饰版本。我的<EditablePopup> 使用<Popup> 作为直接子级,它本质上应该接收 LeafletConsumer 上下文消费者包装器。

我不确定为什么这个组件在我的项目中链接时效果很好,但是在通过 webpack 构建并通过 npm 导入时出现此错误。

【问题讨论】:

【参考方案1】:

我在工作场所遇到了类似的问题,我现在怀疑我们声明 webpack 配置的(类似)方式是罪魁祸首。但对我来说,仅仅改变 libraryTarget 并没有任何效果。在描述了我们的设置后,我的见解如下...

我们有一组可视化组件,我们将它们捆绑到一个“库”中,并导入到一个主项目中。主项目需要获取一些外部数据,但需要在整个库组件的任意位置使用。而且我们不想到处传递道具。所以上下文,对吧?除非提供者(我们在“主”项目中设置和初始化)和消费者位于库边界的不同侧,否则消费者的上下文始终未定义(或者更确切地说,无论“createContext”的默认值是什么...我们没有使用初始化程序)。

不管怎样,快进一堆。如果我从将库中的每个不同组件列为其自己的独立“条目”(在 webpack 文件中)更改为构建一个 index.js 文件来导入和重新导出所有内容 - 并且仅将该文件包含为单数“entry”(在 webpack 中)——然后事情就开始起作用了。

因为 React 上下文 API 需要共享创建的 Context 对象,但共享的概念(我猜无论如何)是在模块级别。它不能是真正全局的。所以不同的入口点、不同的模块、不同的上下文对象。消费者被冷落了。

【讨论】:

这是一个非常有趣的观点。就我而言,libraryTarget 有效,但我只是想导出一个组件。我觉得问题React Context API not working from custom NPM component library 需要同样的答案。请张贴在那里! @robert-lybarger 您对模块级别的上下文有更多了解吗?我遇到了完全相同的问题并按照您的回答解决了它,但是如果我无法分离模块,它会使导入变得不那么干净。有没有办法解决这个问题? @ROODAY -- 恐怕在研究和发布之后不久,我们的项目就被颠覆了(covid-19)。对于我们当时的工作,我们即将放弃“上下文”(至少,跨越模块边界)并修改我们传递状态的方式。 我明白了,感谢您提供的额外信息!就我而言,我真的不希望从上下文切换,因为代码会变得超级重复。但是,这确实给了我一些想法,也许与上下文相关的所有代码都需要来自一个模块,而不使用它的代码可以在另一个模块中?至少可以让我的导入更易于管理。【参考方案2】:

我在Cannot read property 'Component' of undefined - webpack build of react component plugin 中发布了一个受this anwer 启发的答案。我在这里的问题似乎至少与React Context API not working from custom NPM component library 的问题有些相关,而那些人仍在寻找答案——也许这会有所帮助。

我必须对我的webpack.config.jspackage.json 进行调整才能解决这个问题。


webpack.config.js

改变这个:

  libraryTarget: 'commonjs2'

到这里:


      library: "EditablePopup",
      libraryTarget: 'umd'

以及将mode: development 添加到module.exports


package.json

将我的一些dependencies 移动到peerDependencies


    // Change from this:
      "dependencies": 
        "html-react-parser": "^0.10.0",
        "leaflet": "^1.6.0",
        "react": "^16.12.0",
        "react-contenteditable": "^3.3.3",
        "react-dom": "^16.12.0",
        "react-leaflet": "^2.6.1",
        "webpack": "^4.41.5"
      ,
      "peerDependencies": 
        "react": "^16.12.0",
        "react-dom": "^16.12.0",
        "react-leaflet": "^2.6.1",
        "leaflet": "^1.6.0"
      

    // to this:
      "dependencies": 
        "html-react-parser": "^0.10.0",
        "react-contenteditable": "^3.3.3",
        "webpack": "^4.41.5"
      ,
      "peerDependencies": 
        "react": "^16.12.0",
        "react-dom": "^16.12.0",
        "react-leaflet": "^2.6.1",
        "leaflet": "^1.6.0"
      


我很确定 webpack.config.js 的变化是最重要的。为了完整起见,以下是对该文件的更改:

// webpack.config.js var path = require('path'); 模块.exports = 条目:'./src/EditablePopup.js', 输出: 路径:path.resolve(__dirname, 'build'), 文件名:'EditablePopup.js', - libraryTarget: 'commonjs2', + 库:“EditablePopup”, + libraryTarget:'umd' , + 模式:“开发”, 模块: 规则:[ 测试:/\.js$/, 包括:path.resolve(__dirname, 'src'), 排除:/(node_modules|bower_components|build)/, 采用: [ 加载器:'babel-loader', 选项: 预设:['@babel/preset-env'] ] , 测试:/\.css$/, 采用: [ 加载器:'样式加载器', 加载器:'css-loader' ] ] , 外部: 'react-dom': 'commonjs react-dom', 传单: commonjs:'传单', commonjs2: '传单', 根:'L' , '反应传单': commonjs:'反应传单', commonjs2:'反应传单', 根:'ReactLeaflet' , 反应: commonjs:'反应', commonjs2:'反应', 根:'反应' ;

【讨论】:

谢谢赛斯。尽管我正在编译为 TypeScript,但我也提出了类似的问题。希望这能让我朝着正确的方向前进。如果您对compilerOptions 有任何提示,我会很喜欢您的建议。【参考方案3】:

也许您需要将自定义组件包装在withLeaflet 中?我发现this SO answer 在使用react-leaflet 创建自定义组件的步骤中非常清楚。

使用 HOC 将使地图(以及上下文的其余部分)在地图挂载后在您的 createLeafletElement 实现中可用。

【讨论】:

我最初认为可能是这种情况,但结果并非如此。因为组件作为 npm 模块正常工作。我的组件返回一个<Popup />,它已经包含传单上下文。问题在于我如何使用 webpack 编译 JSX 以将其转换为节点模块。不过谢谢你的回答 我明白了。这就说得通了。希望你把它整理好

以上是关于React-leaflet 自定义组件 - 未传递上下文?的主要内容,如果未能解决你的问题,请参考以下文章

React-Leaflet将绘图列表映射到标记

React-Leaflet:将地图控制组件放置在地图之外?

安装 react-leaflet 版本 3 后 React Storybook 未运行

将状态传递给子组件时未定义状态

如何将 useEffect 中的数据向下传递到组件树(未定义)

从 Angular 父组件传递到子组件的数据在子组件中未定义