如何使用 React Hooks 和直接使用 DOM 的对象构建组件? (例如 OpenLayers)?)

How to structure a component using React Hooks with an object that uses the DOM directly? (such as OpenLayers)?)

我正在尝试在我之前基于 class 的组件中使用 React Hooks。该组件如下所示:

class A extends Component {
 constructor(props) {
  super(props)
  this.mapRef = createRef()
  this.map = new Map({ ... })
 }

 componentDidMount() {
  this.map.setTarget(this.mapRef.current)
 }

 // Also shouldComponentUpdate & componentWillUnmount

 render() {
  return (
   <div>
    <div ref={this.mapRef}></div>
    {this.props.children({map: this.map})}
   </div>
  )
 }
}

我对 open-layers 库的理解是,当我创建 Map 对象的实例时,我需要将对 DOM 元素的引用传递给它,因为该库需要直接控制 DOM 元素。我通过 ref.

componentDidMount 函数中执行此操作

尝试将此代码更改为 React Hooks(出于兴趣),我试过这个:

function A (props) {
 var map
 const mapRef = useRef(null)

 useEffect(() => {
  map = new Map()
  map.setTarget(mapRef.current)
 })

  return (
   <div>
    <div ref={mapRef}></div>
    {props.children({map})}
   </div>
  )
}

这只是错误(因为 props.children 函数获取地图对象的 null)。我尝试将地图对象初始化移出函数,这似乎有效:

const map = new Map({ ... })
function A (props) {
 const mapRef = useRef(null)

 useEffect(() => {
  map.setTarget(mapRef.current)
  // Then adjust the map however necessary depending on props
 })

  return (
   <div>
    <div ref={mapRef}></div>
    {props.children({map})}
   </div>
  )
}

这有点管用...尽管似乎 useEffect 回调的触发频率远高于必要的触发频率。我必须弄清楚如何实施 shouldComponentUpdate.

这是使用 React Hooks 的 'correct' 方法吗?我不禁觉得在这种情况下,要么 class 组件更有意义,要么我没有正确使用 Hooks(可能是后者)。

在这种情况下,我实际上根本没有为状态使用 class 组件,而是因为 DOM 更改而使用生命周期方法更新地图实例的能力。

如果您希望 useEffect 挂钩仅在需要时触发,您可以放置​​一个数组,其中包含将触发挂钩的属性作为第二个参数。

useEffect(() => {map.setTarget(...)}, [mapRef.current])

当我尝试使用 OpenLayers 创建自己的地图组件时,这对我来说是一个有用的问题。我使用了一种稍微不同的方法:

olMap.setTarget 方法接受 either an HTML element or the ID of one。所以我构造了初始的OlMap对象,并给目标键赋值undefined。然后在 useEffect 中将目标设置为 div 的 ID。

为了确保此效果仅在组件安装时运行,而不是在每次渲染时运行,将一个空数组作为第二个参数传递给 useEffect。效果 returns 一个在组件卸载时将目标设置回 undefined 的函数。如果地图位于仅在某些路线上呈现的组件中,则此部分是必需的。如果您离开并返回,如果您没有再次设置目标,地图将不会重新呈现。

import React, { useEffect } from 'react';
import { Map, View } from 'ol';
const olMap = new Map({
  target: undefined,
  layers: [
    new TileLayer({
      source: new OSM()
    })
  ],
  view: new View({
    center: [-6005420.749222653, 6000508.181331601],
    zoom: 9
  })
});

export default function OlMap() {
  useEffect(() => {
    olMap.setTarget('map')
    return () => olMap.setTarget(undefined);
  }, []);

  return (
    <div id='map'>
    </div>
  )
}

如果您使用的库需要 HTMLElement,您可以使用 useRefref

export default function OlMap() {
  let mapDiv = useRef(null);
  useEffect(() => {
    olMap.setTarget(mapDiv.current)
    return () => olMap.setTarget(undefined);
  }, []);

  return (
    <div id='map' ref={mapDiv}>
    </div>
  )
}

我再次将第二个参数保留为一个空数组,但是如果 ref 发生变化,如果您想再次调用该效果,您可以在该数组中传递 mapDiv。我不认为这对于 OpenLayers 是必需的,但另一个库可能会进行更改,而不仅仅是附加到目标 HTMLElement