通过改变反应状态来改变反应叶层URL会抛出错误->.TypeError:无法读取未定义的属性"call" [英] Changing the react-leaflet layer url by mutating react state throws error -> TypeError: Cannot read property 'call' of undefined

查看:46
本文介绍了通过改变反应状态来改变反应叶层URL会抛出错误->.TypeError:无法读取未定义的属性"call"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用下面的代码来构建具有可选动态图层URL的地图.

I'm trying to build a map with a selectable and dynamic layer URL with the code below.

当我尝试通过状态更改图层网址时,它会在所附的屏幕截图中显示错误.

When I try to change the layer URL by the state it shows the error in the screenshot attached.

我已将所有切片存储在tiles数组变量中,并且我试图通过更改tile的索引来更改URL.第一次运行项目时,它运行良好,但是当我单击按钮并尝试更改图块的索引以渲染另一个地图图块时,它在屏幕截图中显示了错误.我还要提到的是,开发人员工具chrome的控制台标签中没有显示任何错误.有办法解决这个问题吗?任何想法将不胜感激:)

I have stored all the tiles in the tiles array variable and I'm trying to change the URL by changing the index of the tiles. The first time I run the project it works fine, but when I click the button and try to change the index of the tile to render another map tile it shows the error in the screenshot. I also should mention that there are no errors shown in the console tab of developer tools chrome. Is there a way to solve this problem? Any idea would be appreciated :)

错误的屏幕截图:


const LeafletContainer = () => {
  const [baseViewCoords, setBaseViewCoords] = useState([37.715, 44.8611]);
  const [map, setMap] = useState();

  const merged_20181223 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20180924 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20190323 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20190626 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20190919 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20191218 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20200625 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const merged_20200918 = L.tileLayer(
    "dymmyurl",
    { tms: true, opacity: 1, attribution: "", minZoom: 1, maxZoom: 16 }
  );

  const tiles = [
    merged_20190323,
    merged_20191218,
    merged_20181223,
    merged_20180924,
    merged_20190626,
    merged_20190919,
    merged_20200625,
    merged_20200918,
  ];

  const [leftTileIndex, setLeftTileIndex] = useState(0);
  const [rightTileIndex, setTRightTileIndex] = useState(7);

  const changeTileHandler = () => {
    setLeftTileIndex((prev) => {
      if (leftTileIndex === tiles.length - 1) {
        return 0;
      }
      return prev + 1;
    });
  };

  useEffect(() => {
    if (map) {
      L.control
        .splitMap(
          L.tileLayer(tiles[leftTileIndex]._url, {
            tms: true,
            opacity: 1,
            attribution: "",
            minZoom: 1,
            maxZoom: 16,
          }).addTo(map),
          L.tileLayer(tiles[rightTileIndex]._url, {
            tms: true,
            opacity: 1,
            attribution: "",
            minZoom: 1,
            maxZoom: 16,
          })
            .addTo(map)
            .addTo(map)
        )
        .addTo(map);
    }
  }, [map, leftTileIndex, rightTileIndex]);

  useEffect(() => {
    if (map) {
      map.setView(baseViewCoords);
    }
  }, [map, baseViewCoords]);

  return (
    <div style={{ position: "relative" }}>
      <SearchArea
        {...{
          changeTileHandler,
        }}
      />
      <Options />
      <Coordinate {...{ baseViewCoords }} />
      <div id="map">
        <MapContainer
          style={{ height: "100%" }}
          center={baseViewCoords}
          zoom={9}
          scrollWheelZoom={true}
          whenCreated={(map) => {
            setMap(map);
          }}
        >
          <TileLayer
            tms={true}
            minZoom={1}
            maxZoom={16}
            opacity={1}
            attribution=""
            url={tiles[leftTileIndex]._url}
          />

          <TileLayer
            minZoom={1}
            maxZoom={16}
            opacity={1}
            tms={true}
            attribution=""
            url={tiles[rightTileIndex]._url}
          />
        </MapContainer>
      </div>
    </div>
  );
};

export default LeafletContainer;

推荐答案

我不确定这个错误为什么会像小叶的 Events 类那样严重,但是我确实看到了一个主要问题.在useEffect中,每次用户触发 setLeftTileIndex setRightTileIndex 的状态更改时,即为 L.Control.splitmap 的新实例. 已添加到地图中.在编写react-leaflet < TileLayer/> 组件以处理url更改并自动更新底层的 L.TileLayer 时,即您的 L版本.control.splitmap 不是-只是添加一个新的splitmap.同时,旧的是指不再存在的L.TileLayer.您应使用 leaflet-splitmap setLeftLayers setRightLayers 方法:

I'm not sure exactly why this error is getting as deep as leafet's Events class, but I do see one main problem. In your useEffect, every time the user triggers a state change that setLeftTileIndex or setRightTileIndex, a new instance of L.Control.splitmap is added to the map. While the react-leaflet <TileLayer /> component is written to handle the url change and automatically update the underlying L.TileLayer, your version of L.control.splitmap is not - it is simply adding a new splitmap. Meanwhile, the old one is referring to L.TileLayer's that no longer exist. You should use leaflet-splitmap's setLeftLayers and setRightLayers methods:

// initialize this outside the useEffect
const [splitMapAdded, setSplitMapAdded] = useState(false)

const splitMap = L.control.splitMap(
  L.tileLayer("left_side_initial_url", { options }),
  L.tileLayer("right_side_initial_url", { options })
)

useEffect(() => {
  if (map && !splitMapAdded) {
    splitmap.addTo(map);
    setSplitMapAdded()true
  }
}, [map]);

// Set the splitmap left side when leftTileIndex changes
useEffect(() => {
  if (map && splitMapAdded) {
    splitMap.setLeftLayers(
      [L.tileLayer(tiles[leftTileIndex]._url], 
      { options }
    )
  }
}, [map, leftTileIndex])

// Set the splitmap left side when leftTileIndex changes
useEffect(() => {
  if (map && splitMapAdded) {
    splitMap.setRightLayers(
      [L.tileLayer(tiles[rightTileIndex]._url], 
      { options }
    )
  }
}, [map, rightTileIndex])

因此,现在将splitmap控件添加到具有某些初始值的组件安装中.当这些状态变量发生更改时,您无需使用重新创建控件的方法,而使用其内部方法来更改url.希望这会帮助您开始挖掘其中的一些错误.

So now the splitmap control is added on component mount with some initial values. When those state variables change, instead of recreating the control, you use its internal methods to change the urls. Hopefully this will get you started in digging out some of those errors.

我还可以补充一点,可以通过创建react-leaflet v3自定义组件来对其进行整洁的管理.您可以使用创建函数来创建组件:

I also might add that this can be neatly managed by creating a react-leaflet v3 custom component. You can create the component with a create function:

const createSplitMap = (props, context) => {

  const instance = L.control.splitmap(
    L.tileLayer(props.leftTileLayerUrl, leftTileLayerOptions),
    L.tileLayer(props.rightTileLayerUrl, rightTileLayerOptions)
  )

  return { instance, context }

}

然后您的更新功能可能如下所示:

Then your update function might look like this:

const updateSplitMap = (instance, props, prevProps) => {

  if (prevProps.leftTileLayerUrl !== props.leftTileLayerUrl){
    instance.setLeftLayers(L.tileLayer(props.leftTileLayerUrl))
  }

  if (prevProps.rightTileLayerUrl !== props.rightTileLayerUrl){
    instance.setLeftLayers(L.tileLayer(props.rightTileLayerUrl))
  }

}

要整合在一起,可以使用 createLayerComponent 工厂功能:

To put it all together, you can use the createLayerComponent factory function:

const SplitMap = createLayerComponent(createSplitMap, updateSplitMap);
export SplitMap

现在在地图中,您可以使用此组件:

Now in your map, you can use this component:

<MapContainer {...mapContainerProps}>
  <SplitMap 
    rightTileLayerUrl={tiles[rightTileIndex]._url}
    leftTileLayerUrl={tiles[leftTileIndex]._url}
  />
  <TileLayer {...tileLayer1Props} />
  <TileLayer {...tileLayer2Props} />
</MapContainer>

我还没有测试过,但这是您用来为splitmap创建react-leaflet自定义组件的常规模式.

I haven't tested this, but this is the general pattern you would use to create a react-leaflet custom component for the splitmap.

这篇关于通过改变反应状态来改变反应叶层URL会抛出错误->.TypeError:无法读取未定义的属性"call"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆