【问题标题】:Subscriber pattern with RecoilJS (React)使用 RecoilJS (React) 的订阅者模式
【发布时间】:2021-05-18 08:56:23
【问题描述】:

我正在使用 React、TS、Mapbox、react-mapbox-gl 和 RecoilJS 构建一个应用程序。

这是我正在使用的Dashboard 组件:

import React from "react";
import PageTemplate from "../../components/PageTemplate/PageTemplate";

import ReactMapboxGl from "react-mapbox-gl";

import { CGOIncidentsMapConfig } from "./Map/CGOIncidents";
import { zoomSuscriberState, clickSuscriberState } from "../../contexts/MapContext";
import { useRecoilValue } from "recoil";
import { MapEvent } from "react-mapbox-gl/lib/map-events";

const Map = ReactMapboxGl({
  accessToken: process.env.REACT_APP_MAPBOX_TOKEN ?? "",
});

const getEventHandle: (subscribers: MapEvent[]) => MapEvent = (subscribers) => (map, evt) => {
  console.log(subscribers);
  for (const event of subscribers) {
    event(map, evt);
  }
};

const Dashboard: React.FC = () => {
  const zoomSuscribers = useRecoilValue(zoomSuscriberState);
  const clickSuscribers = useRecoilValue(clickSuscriberState);

  console.log(zoomSuscribers);
  console.log(clickSuscribers);

  return (
    <PageTemplate>
      <Map
        style="mapbox://styles/mapbox/light-v10"
        containerStyle={{
          height: "100%",
          width: "100%",
        }}
        center={[-3.7, 40.44]}
        zoom={[5]}
        onZoom={getEventHandle(zoomSuscribers)}
        onClick={getEventHandle(clickSuscribers)}
      >
        <CGOIncidentsMapConfig />
      </Map>
    </PageTemplate>
  );
};

export default Dashboard;

这是CGOIncidentsMapConfig 组件的简化:


export const CGOIncidentsMapConfig: React.FC = () => {
  const [popupCoordinates, setPopupCoordinates] = useState<number[]>([-0.13235092163085938, 51.518250335096376]);
  const [isPopupVisible, setPopupVisible] = useState<boolean>(false);
  const [popopContainer, setPopupContainer] = useState("");
  const [leyend, setLeyend] = useState<{
    title: string;
    elements: {
      title: string;
      color: string;
    }[];
  }>(LEYENDS.PROVINCES);

  const onZoomMapEvent: MapEvent = (map) => {
    console.log("ZIIIM", map.getZoom());
  };

  const onMouseEnterEvent = (e: MapLayerMouseEvent) => {
    const map = e.target;
    map.getCanvas().style.cursor = "pointer";
  };

  const onMouseLeaveEvent = (e: MapLayerMouseEvent) => {
    const map = e.target;
    map.getCanvas().style.cursor = "";
  };

  const dismissPopup = () => {
    setPopupVisible(false);
    console.log("CIIM");
  };

  const setZoomSuscribers = useSetRecoilState(zoomSuscriberState);
  const setClickSuscribers = useSetRecoilState(clickSuscriberState);

  useEffect(() => {
    setZoomSuscribers((oldZoomSubscribers) => [...oldZoomSubscribers, onZoomMapEvent]);

    setClickSuscribers((oldClickSubscribers) => [...oldClickSubscribers, dismissPopup]);

    console.log("updated recoil");
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  console.log(isPopupVisible);

  return (
    <Fragment>
      <Source id={CGO_INCIDENTS_SOURCE_ID} tileJsonSource={CGO_INCIDENTS_SOURCE_OPTIONS} />
      <Layer
        id={CGO_INCIDENTS_LAYER_LAYOUT.id}
        type={CGO_INCIDENTS_LAYER_LAYOUT.type}
        sourceId={CGO_INCIDENTS_LAYER_LAYOUT.source}
        sourceLayer={CGO_INCIDENTS_LAYER_LAYOUT["source-layer"]}
        paint={CGO_INCIDENTS_LAYER_LAYOUT.paint}
        onClick={onClickEvent}
        onMouseEnter={onMouseEnterEvent}
        onMouseLeave={onMouseLeaveEvent}
      />
      {isPopupVisible && (
        <Popup coordinates={popupCoordinates} onClick={dismissPopup}>
          {popopContainer}
        </Popup>
      )}
      <Leyend title={leyend.title} elements={leyend.elements} />
    </Fragment>
  );
};

这是我在其中创建原子的MapContext 文件:

import { MapEvent } from "react-mapbox-gl/lib/map-events";
import { atom } from "recoil";

export const zoomSuscriberState = atom<MapEvent[]>({ key: "zoomSuscriberState", default: [] });
export const clickSuscriberState = atom<MapEvent[]>({ key: "clickSuscriberState", default: [] });

如您所见,我想做的只是使用 RecoilJS 为 onClick 和 onZoom 事件创建订阅者模式。我的想法是将每个 Mapbox 层分隔在不同的组件中。

getEventHandler返回的函数被执行时,里面的console.log只显示zoomSuscribersclickSuscribers的空数组,但是我在Dashboard组件中看到console.log两者都是atom 有一个由CGOIncidentsMapConfig 组件解决的回调函数。这些原子在Dashboard 的第一次渲染时为空,并在CGOIncidentsMapConfig 组件被渲染后完成。似乎 RecoilJS 更新了 atom 和 Dashboard 组件,正如预期的那样。

为什么getEventHandler 函数中的console.log 只打印一个空数组?因此,订阅者没有正确执行该事件。

提前致谢。

【问题讨论】:

    标签: reactjs typescript design-patterns mapbox recoiljs


    【解决方案1】:

    这是因为您调用的是getEventHandle 函数,而不是将其传递给Map 组件。

    更改这些行:

    onZoom={getEventHandle(zoomSuscribers)}
    onClick={getEventHandle(clickSuscribers)}
    

    onZoom={() => getEventHandle(zoomSuscribers)}
    onClick={() => getEventHandle(clickSuscribers)}
    

    【讨论】:

    • 首先,感谢您的回复。遗憾的是它没有意义:onZoomonClick 是需要具有两个参数的函数的属性:map 和 event。您提出的更改应该是这样的:``` onZoom={(map,evt) => getEventHandle(zoomSuscribers)(map,evt)} onClick={(map,evit) => getEventHandle(clickSuscribers)(map ,evt)} ``` 遗憾的是,这并不能解决我的问题,因为它是一样的。无论如何感谢您的时间:)
    【解决方案2】:

    我解决了!

    useRecoilCallback的解决方法:

    import React from "react";
    import PageTemplate from "../../components/PageTemplate/PageTemplate";
    
    import ReactMapboxGl from "react-mapbox-gl";
    
    import { CGOIncidentsMapConfig } from "./Map/CGOIncidents";
    import { zoomSuscriberState, clickSuscriberState } from "../../contexts/MapContext";
    import { useRecoilCallback, useRecoilValue } from "recoil";
    import { MapEvent } from "react-mapbox-gl/lib/map-events";
    
    const Map = ReactMapboxGl({
      accessToken: process.env.REACT_APP_MAPBOX_TOKEN ?? "",
    });
    
    const Dashboard: React.FC = () => {
      const onZoom: MapEvent = useRecoilCallback(({ snapshot }) => async (map, evt) => {
        const subs = await snapshot.getPromise(zoomSuscriberState);
        for (const event of subs) {
          event(map, evt);
        }
      });
    
      const onClick: MapEvent = useRecoilCallback(({ snapshot }) => async (map, evt) => {
        const subs = await snapshot.getPromise(clickSuscriberState);
        for (const event of subs) {
          event(map, evt);
        }
      });
    
      return (
        <PageTemplate>
          <Map
            style="mapbox://styles/mapbox/light-v10"
            containerStyle={{
              height: "100%",
              width: "100%",
            }}
            center={[-3.7, 40.44]}
            zoom={[5]}
            onZoom={onZoom}
            onClick={onClick}
          >
            <CGOIncidentsMapConfig />
          </Map>
        </PageTemplate>
      );
    };
    
    export default Dashboard;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-23
      • 2016-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-14
      • 2019-11-09
      相关资源
      最近更新 更多