Skip to content

Instantly share code, notes, and snippets.

@sebastianfrey
Created December 7, 2022 12:24
Show Gist options
  • Save sebastianfrey/017d7f8661bafd54d1217ffc2c323edc to your computer and use it in GitHub Desktop.
Save sebastianfrey/017d7f8661bafd54d1217ffc2c323edc to your computer and use it in GitHub Desktop.
Connect @arcgis/core to React
import React, { useEffect, useState, useMemo } from 'react';
import { useEvents, usePrevious, useWatchables, WatchKeys } from '@moss/react-hooks';
import { LayerBaseProps } from './interfaces';
import useMap from '../../hooks/useMap';
import isEqual from 'lodash-es/isEqual';
const defaultOnProp = {};
const defaultWatchProp = {};
export type LayerConstructor<T> = new (...params: any[]) => T;
/**
* Utility to wrap ArcGIS JS API Layers as React Components.
*/
export default function makeLayer<
P,
L extends __esri.Layer = __esri.Layer,
K extends WatchKeys<L> = WatchKeys<L>,
>(Constructor: LayerConstructor<L>, displayName?: string): React.FC<P & LayerBaseProps<L, K, P>> {
const BaseLayer: React.FC<P & LayerBaseProps<L, K, P>> = ({
on: onProp = defaultOnProp,
watch: watchProp = defaultWatchProp,
children,
layer: groupLayerFromProps,
onMount,
onUnmount,
position,
initialProperties,
...props
}) => {
const map = useMap();
const on = useMemo(() => onProp, [onProp]);
const watch = useMemo(() => watchProp, [watchProp]);
const [layer, setLayer] = useState<L>();
const callbackRefs = React.useRef({
onMount,
onUnmount,
});
React.useEffect(() => {
callbackRefs.current = {
onMount,
onUnmount,
};
});
const previousProps = usePrevious(props);
const propsRef = React.useRef(props ?? {});
const initialPropertiesRef = React.useRef(initialProperties ?? {});
// Initialize layer effect
useEffect(() => {
const layer = new Constructor({
...propsRef.current,
...initialPropertiesRef.current,
});
setLayer(layer);
return () => setLayer(undefined);
}, []);
// Hook layer into groupLayer
useEffect(() => {
const groupLayer = groupLayerFromProps ?? map?.layers;
if (layer && groupLayer) {
groupLayer.add(layer, position);
if (callbackRefs.current.onMount) {
callbackRefs.current.onMount(layer);
}
}
return () => {
if (layer && groupLayer) {
groupLayer.remove(layer);
if (callbackRefs.current.onUnmount) {
callbackRefs.current.onUnmount(layer);
}
}
};
}, [groupLayerFromProps, map, layer, position]);
// Append props to layer on every mount.
useEffect(() => {
if (layer && !isEqual(props, previousProps)) {
Object.entries(props).forEach(([prop, value]) => {
if (!isEqual((layer as any)[prop], value)) {
(layer as any)[prop] = value;
}
});
}
});
useEvents(on, layer);
useWatchables(watch, layer);
return (
<>
{layer &&
React.Children.map(children, (child: any) =>
React.cloneElement(child, {
layer,
}),
)}
</>
);
};
BaseLayer.displayName = displayName || 'BaseLayer';
return BaseLayer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment