首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >React检测到了ForwardRef(ContainerComponent)调用的钩子顺序的变化。

React检测到了ForwardRef(ContainerComponent)调用的钩子顺序的变化。
EN

Stack Overflow用户
提问于 2022-11-10 04:23:29
回答 1查看 71关注 0票数 1

我正在使用react传单构建一个应用程序,我最近更新了我所有的依赖项,这导致了一个我无法解决的错误。

错误:

代码语言:javascript
运行
复制
React has detected a change in the order of Hooks called by ForwardRef(ContainerComponent)

从我的应用程序中删除<MapContainer>组件修复了错误,但我似乎不知道ForwardRef组件是在哪里呈现的,或者为什么在呈现之间钩子的顺序会发生变化。

这是我的组成部分:

代码语言:javascript
运行
复制
const Map = ({ openModal }) => {

    const [homeCoords, setHomeCoords] = useState([49.2, -123]);
    const [bounds, setBounds] = useState({
        lat_lower: 48.9,
        lat_upper: 49.5,
        lon_left: -123.8,
        lon_right: -122.2
    });


    // Get the user's location with navigator.geolocation
    useEffect(() => {
        if(!window.navigator.geolocation) {
            return;
        }

        window.navigator.geolocation.getCurrentPosition(
            // success
            (res) => {
                setHomeCoords([res.coords.latitude, res.coords.longitude]);
            },

            // failure
            () => {
                console.error('Must allow pestlocations.com to access your location to use this feature.')
            }
        )
    }, []);


    // Helper function for BoundTracker component 
    const collectBounds = (e) => {
        const bounds = e.target.getBounds();
                
        const currLatLower = bounds._southWest.lat;
        const currLatUpper = bounds._northEast.lat;
        const currLonLeft = bounds._southWest.lng;
        const currLonRight = bounds._northEast.lng;

        setBounds({
            lat_lower: currLatLower,
            lat_upper: currLatUpper,
            lon_left: currLonLeft,
            lon_right: currLonRight
        })
    }


    // Listen for dragging or zooming on map and update bounds
    const BoundTracker = () => {
        useMapEvents({
            
            // Drag map
            dragend: (e) => {
                collectBounds(e);
            },

            // Zoom map
            zoomend: (e) => {
                collectBounds(e);
            }
        })
    }

    const HomeButton = () => {

        const map = useMap();

        return (
            <div className="btn home" aria-disabled="false" onClick={() => {
                map.panTo(homeCoords);

                const bounds = map.getBounds();
                setBounds({
                    lat_lower: bounds._southWest.lat,
                    lat_upper: bounds._northEast.lat,
                    lon_left: bounds._southWest.lng,
                    lon_right: bounds._northEast.lng
                })
            }}>
                <AiFillHome />
            </div>
        )
    }

    return (
        <>
            <MapContainer className="Map" position={homeCoords} zoom={10}>
                <TileLayer
                    attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                />
                
                <HomeButton />
                <div className="btn info" onClick={() => openModal("Welcome")}><AiOutlineInfoCircle /></div>
                <div className="btn legend" onClick={() => openModal("Legend")}><BsMap /></div>
                <div className="search-btn" onClick={() => return}>Search This Area</div>
                
                <PointClusters />
                <BoundTracker />
            </MapContainer>
        </>
    )
}

编辑这里是我的PointClusters组件:

代码语言:javascript
运行
复制
import { useState } from 'react';
import MarkerClusterGroup from 'react-leaflet-cluster';
import { Marker, Popup } from 'react-leaflet';
import L from 'leaflet';
import './PointClusters.css';


// For testing purposes only - - - - - - - - - - - - - - - - - -
const testPoints = require('../../../testPoints.json').features;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



const PointClusters = () => {

    // Initialize points as testpoints. ** CHANGE FOR PRODUCTION **
    const [points, setPoints] = useState(testPoints);

    const newicon = new L.divIcon({
        className: "custom-marker",
        html: "<span class='arrow'></span>"
    });

    const createClusterCustomIcon = function (cluster) {
        return L.divIcon({
          html: `<span>${cluster.getChildCount()}</span>`,
          className: 'custom-marker-cluster',
          iconSize: L.point(33, 33, true),
        })
      }

    return (
        <MarkerClusterGroup
            iconCreateFunction={createClusterCustomIcon}
        >
            {
                points.map((point, i) => {

                    const { name, address, type, numOfReports, url } = point.properties;

                    const coords = [point.geometry.coordinates[1], point.geometry.coordinates[0]];

                    return (
                        <Marker position={coords} icon={newicon} key={i}>
                            <Popup>
                                {
                                    name === "None" ? null : 
                                    
                                    <>
                                        <b>{name}</b>
                                        <br />
                                    </>
                                }
                                <strong>Address</strong>: {address}
                                <br />
                                <strong>Type</strong>: {type}
                                <hr />
                                At least <a href={url}>{numOfReports} reports</a> on bedbugregistry.com
                            </Popup>
                        </Marker>
                    )
                })
            }
        </MarkerClusterGroup>
    )
}
EN

Stack Overflow用户

回答已采纳

发布于 2022-11-11 09:55:51

由于只有useState钩子在PointClusters中,所以我假设这里的问题是react传单-集群包。我知道当我想使用它的时候,它没有得到f r的支持--传单4。它现在有了一个版本2.0.0,应该是兼容的,但是看看他们在解决方案中使用钩子的代码。

因为他们不支持反作用-传单4,当我需要它时,我决定调整实际的代码和修改工作,以满足我的需要。以下是适应情况:

代码语言:javascript
运行
复制
import { createPathComponent } from "@react-leaflet/core";
import L, { LeafletMouseEventHandlerFn } from "leaflet";
import "leaflet.markercluster";
import { ReactElement, useMemo } from "react";
import { Building, BuildingStore, Circle } from "tabler-icons-react";
import { createLeafletIcon } from "./utils";
import styles from "./LeafletMarkerCluster.module.css";
import "leaflet.markercluster/dist/MarkerCluster.css";
type ClusterType = { [key in string]: any };

type ClusterEvents = {
  onClick?: LeafletMouseEventHandlerFn;
  onDblClick?: LeafletMouseEventHandlerFn;
  onMouseDown?: LeafletMouseEventHandlerFn;
  onMouseUp?: LeafletMouseEventHandlerFn;
  onMouseOver?: LeafletMouseEventHandlerFn;
  onMouseOut?: LeafletMouseEventHandlerFn;
  onContextMenu?: LeafletMouseEventHandlerFn;
};

// Leaflet is badly typed, if more props needed add them to the interface.
// Look in this file to see what is available.
// node_modules/@types/leaflet.markercluster/index.d.ts
// MarkerClusterGroupOptions
export interface LeafletMarkerClusterProps {
  spiderfyOnMaxZoom?: boolean;
  children: React.ReactNode;
  size?: number;
  icon?: ReactElement;
}
const createMarkerCluster = (
  {
    children: _c,
    size = 30,
    icon = <Circle size={size} />,
    ...props
  }: LeafletMarkerClusterProps,
  context: any
) => {
  const markerIcons = {
    default: <Circle size={size} />,
    property: <Building size={size} />,
    business: <BuildingStore size={size} />,
  } as { [key in string]: ReactElement };

  const clusterProps: ClusterType = {
    iconCreateFunction: (cluster: any) => {
      const markers = cluster.getAllChildMarkers();

      const types = markers.reduce(
        (
          acc: { [x: string]: number },
          marker: {
            key: string;
            options: { icon: { options: { className: string } } };
          }
        ) => {
          const key = marker?.key || "";
          const type =
            marker.options.icon.options.className || key.split("-")[0];
          const increment = (key.split("-")[1] as unknown as number) || 1;

          if (type in markerIcons) {
            return { ...acc, [type]: (acc[type] || 0) + increment };
          }
          return { ...acc, default: (acc.default || 0) + increment };
        },
        {}
      ) as { [key in string]: number };
      const typeIcons = Object.entries(types).map(([type, count], index) => {
        if (count > 0) {
          const typeIcon = markerIcons[type];
          return (
            <div key={`${type}-${count}`} style={{ display: "flex" }}>
              <span>{typeIcon}</span>
              <span style={{ width: "max-content" }}>{count}</span>
            </div>
          );
        }
      });
      const iconWidth = typeIcons.length * size;

      return createLeafletIcon(
        <div style={{ display: "flex" }} className={"cluster-marker"}>
          {typeIcons}
        </div>,
        iconWidth,
        undefined,
        iconWidth,
        30
      );
    },
    showCoverageOnHover: false,
    animate: true,
    animateAddingMarkers: false,
    removeOutsideVisibleBounds: false,
  };
  const clusterEvents: ClusterType = {};
  // Splitting props and events to different objects
  Object.entries(props).forEach(([propName, prop]) =>
    propName.startsWith("on")
      ? (clusterEvents[propName] = prop)
      : (clusterProps[propName] = prop)
  );

  const instance = new (L as any).MarkerClusterGroup(clusterProps);

  instance.on("spiderfied", (e: any) => {
    e.cluster._icon?.classList.add(styles.spiderfied);
  });
  instance.on("unspiderfied", (e: any) => {
    e.cluster._icon?.classList.remove(styles.spiderfied);
  });

  // This is not used at the moment, but could be used to add events to the cluster.
  // Initializing event listeners
  Object.entries(clusterEvents).forEach(([eventAsProp, callback]) => {
    const clusterEvent = `cluster${eventAsProp.substring(2).toLowerCase()}`;
    instance.on(clusterEvent, callback);
  });
  return {
    instance,
    context: {
      ...context,
      layerContainer: instance,
    },
  };
};
const updateMarkerCluster = (instance: any, props: any, prevProps: any) => {};
const LeafletMarkerCluster = createPathComponent(
  createMarkerCluster,
  updateMarkerCluster
);

const LeafletMarkerClusterWrapper: React.FC<LeafletMarkerClusterProps> = ({
  children,
  ...props
}) => {
  const markerCluster = useMemo(() => {
    return <LeafletMarkerCluster>{children}</LeafletMarkerCluster>;
  }, [children]);
  return <>{markerCluster}</>;
};

export default LeafletMarkerClusterWrapper;

我组合了不同类型的标记,并用集群中的数字显示每个图标。您应该能够替换iconCreateFunction以满足您的需要。

createLeafletIcon看起来如下所示:

代码语言:javascript
运行
复制
import { divIcon } from "leaflet";
import { ReactElement } from "react";
import { renderToString } from "react-dom/server";

export const createLeafletIcon = (
  icon: ReactElement,
  size: number,
  className?: string,
  width: number = size,
  height: number = size
) => {
  return divIcon({
    html: renderToString(icon),
    iconSize: [width, height],
    iconAnchor: [width / 2, height],
    popupAnchor: [0, -height],
    className: className ? className : "",
  });
};

另一个技巧是研究如何在道具上使用useMemo,否则系统可能会将其视为“新的”,但是第一个操作顺序应该使其工作,然后您可以尝试查找导致重发的道具。祝你好运,如果你有任何关于执行的问题,请告诉我。

票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74384119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档