Skip to content

Commit

Permalink
Refactor useViewportFeatures (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio Clebal authored Dec 13, 2021
1 parent ffdf939 commit ed5ce91
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 198 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Implement C4R filtering using binary data [#228](https://github.com/CartoDB/carto-react/pull/228)
- Fix build adding peerDependecy of @carto/react-core to @carto/react-ui [#237](https://github.com/CartoDB/carto-react/pull/237)
- Refactor useViewportFeatures [#238](https://github.com/CartoDB/carto-react/pull/238)

## 1.1.3 (2021-12-04)

Expand Down
33 changes: 23 additions & 10 deletions packages/react-api/src/hooks/useCartoLayerProps.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { DataFilterExtension } from '@deck.gl/extensions';
import { _buildFeatureFilter } from '@carto/react-core';
import useViewportFeatures from './useViewportFeatures';
import { MAP_TYPES, API_VERSIONS } from '@deck.gl/carto';
import { useSelector } from 'react-redux';
import useGeoJsonFeatures from './useGeoJsonFeatures';
import useTilesetFeatures from './useTilesetFeatures';

export default function useCartoLayerProps({
source,
uniqueIdProperty,
viewportFeatures = true,
viewporFeaturesDebounceTimeout = 500
viewporFeaturesDebounceTimeout = 250
}) {
const [onViewportLoad, onDataLoad, fetch] = useViewportFeatures(
const viewport = useSelector((state) => state.carto.viewport);

const [onDataLoad] = useGeoJsonFeatures({
source,
viewport,
uniqueIdProperty,
viewporFeaturesDebounceTimeout
);
debounceTimeout: viewporFeaturesDebounceTimeout
});

const [onViewportLoad, fetch] = useTilesetFeatures({
source,
viewport,
uniqueIdProperty,
debounceTimeout: viewporFeaturesDebounceTimeout
});

let props = {};

Expand All @@ -23,13 +35,14 @@ export default function useCartoLayerProps({
) {
props = {
binary: true,
onViewportLoad: viewportFeatures ? onViewportLoad : null,
fetch: viewportFeatures ? fetch : null
...(viewportFeatures && {
onViewportLoad,
fetch
})
};
} else if (source?.type === MAP_TYPES.QUERY || source?.type === MAP_TYPES.TABLE) {
props = {
// empty function should be removed by null, but need a fix in CartoLayer
onDataLoad: viewportFeatures ? onDataLoad : () => null
props = viewportFeatures && {
onDataLoad
};
}

Expand Down
48 changes: 48 additions & 0 deletions packages/react-api/src/hooks/useFeaturesCommons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useDispatch } from 'react-redux';
import { setViewportFeaturesReady } from '@carto/react-redux';
import { useState, useRef, useEffect, useCallback } from 'react';

export default function useFeaturesCommons({ source }) {
const dispatch = useDispatch();

const [isDataLoaded, setDataLoaded] = useState(false);
const debounceIdRef = useRef(null);

useEffect(() => {
if (!source) {
setDataLoaded(false);
}
}, [source]);

const clearDebounce = useCallback(() => {
if (debounceIdRef.current) {
clearTimeout(debounceIdRef.current);
}
debounceIdRef.current = null;
}, []);

const stopAnyCompute = useCallback(() => {
clearDebounce();
setDataLoaded(false);
}, [clearDebounce, setDataLoaded]);

const sourceId = source?.id;

const setSourceViewportFeaturesReady = useCallback(
(ready) => {
if (sourceId) {
dispatch(setViewportFeaturesReady({ sourceId, ready }));
}
},
[dispatch, sourceId]
);

return [
debounceIdRef,
isDataLoaded,
setDataLoaded,
clearDebounce,
stopAnyCompute,
setSourceViewportFeaturesReady
];
}
76 changes: 76 additions & 0 deletions packages/react-api/src/hooks/useGeoJsonFeatures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useEffect, useCallback } from 'react';
import { debounce } from '@carto/react-core';
import { Methods, executeTask } from '@carto/react-workers';
import { throwError } from './utils';
import useFeaturesCommons from './useFeaturesCommons';

export default function useGeoJsonFeatures({
source,
viewport,
uniqueIdProperty = 'cartodb_id',
debounceTimeout = 250
}) {
const [
debounceIdRef,
isGeoJsonLoaded,
setGeoJsonLoaded,
clearDebounce,
stopAnyCompute,
setSourceViewportFeaturesReady
] = useFeaturesCommons({ source });

const sourceId = source?.id;

const computeFeaturesGeoJson = useCallback(
({ viewport, uniqueIdProperty }) => {
executeTask(sourceId, Methods.VIEWPORT_FEATURES_GEOJSON, {
viewport,
uniqueIdProperty
})
.then(() => {
setSourceViewportFeaturesReady(true);
})
.catch(throwError);
},
[setSourceViewportFeaturesReady, sourceId]
);

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedComputeFeaturesGeoJson = useCallback(
debounce(computeFeaturesGeoJson, debounceTimeout),
[computeFeaturesGeoJson]
);

useEffect(() => {
if (sourceId && isGeoJsonLoaded) {
clearDebounce();
setSourceViewportFeaturesReady(false);
debounceIdRef.current = debouncedComputeFeaturesGeoJson({
viewport,
uniqueIdProperty
});
}
}, [
viewport,
uniqueIdProperty,
sourceId,
isGeoJsonLoaded,
debouncedComputeFeaturesGeoJson,
setSourceViewportFeaturesReady,
clearDebounce,
debounceIdRef
]);

const onDataLoad = useCallback(
(geojson) => {
stopAnyCompute();
setSourceViewportFeaturesReady(false);
executeTask(sourceId, Methods.LOAD_GEOJSON_FEATURES, { geojson })
.then(() => setGeoJsonLoaded(true))
.catch(throwError);
},
[sourceId, setSourceViewportFeaturesReady, stopAnyCompute, setGeoJsonLoaded]
);

return [onDataLoad];
}
111 changes: 111 additions & 0 deletions packages/react-api/src/hooks/useTilesetFeatures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useEffect, useCallback } from 'react';
import { debounce } from '@carto/react-core';
import { Methods, executeTask } from '@carto/react-workers';
import { Layer } from '@deck.gl/core';
import { throwError } from './utils';
import useFeaturesCommons from './useFeaturesCommons';

export default function useTilesetFeatures({
source,
viewport,
uniqueIdProperty,
debounceTimeout = 250
}) {
const [
debounceIdRef,
isTilesetLoaded,
setTilesetLoaded,
clearDebounce,
stopAnyCompute,
setSourceViewportFeaturesReady
] = useFeaturesCommons({ source });

const sourceId = source?.id;

const computeViewportFeatures = useCallback(
({ viewport, uniqueIdProperty }) => {
setSourceViewportFeaturesReady(false);

executeTask(sourceId, Methods.VIEWPORT_FEATURES, {
viewport,
uniqueIdProperty
})
.then(() => {
setSourceViewportFeaturesReady(true);
})
.catch(throwError)
.finally(clearDebounce);
},
[setSourceViewportFeaturesReady, sourceId, clearDebounce]
);

const loadTiles = useCallback(
(tiles) => {
const cleanedTiles = tiles.reduce((acc, { data, isVisible, bbox }) => {
if (isVisible && data) {
acc.push({
data,
bbox
});
}
return acc;
}, []);

executeTask(sourceId, Methods.LOAD_TILES, { tiles: cleanedTiles })
.then(() => setTilesetLoaded(true))
.catch(throwError);
},
[sourceId, setTilesetLoaded]
);

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedComputeViewportFeatures = useCallback(
debounce(computeViewportFeatures, debounceTimeout),
[computeViewportFeatures]
);

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedLoadTiles = useCallback(debounce(loadTiles, debounceTimeout), [
loadTiles
]);

useEffect(() => {
if (sourceId && isTilesetLoaded) {
clearDebounce();
setSourceViewportFeaturesReady(false);
debounceIdRef.current = debouncedComputeViewportFeatures({
viewport,
uniqueIdProperty
});
}
}, [
viewport,
uniqueIdProperty,
debouncedComputeViewportFeatures,
sourceId,
isTilesetLoaded,
setSourceViewportFeaturesReady,
clearDebounce,
debounceIdRef
]);

const onViewportLoad = useCallback(
(tiles) => {
stopAnyCompute();
setSourceViewportFeaturesReady(false);

debounceIdRef.current = debouncedLoadTiles(tiles);
},
[stopAnyCompute, setSourceViewportFeaturesReady, debouncedLoadTiles, debounceIdRef]
);

const fetch = useCallback(
(...args) => {
stopAnyCompute();
return Layer.defaultProps.fetch.value(...args);
},
[stopAnyCompute]
);

return [onViewportLoad, fetch];
}
Loading

0 comments on commit ed5ce91

Please sign in to comment.