Created
December 7, 2022 12:24
-
-
Save sebastianfrey/017d7f8661bafd54d1217ffc2c323edc to your computer and use it in GitHub Desktop.
Connect @arcgis/core to React
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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