Skip to content

Commit

Permalink
chore(react,vue): Reorganize hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Sep 6, 2024
1 parent 9590fa9 commit b98b87a
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Card } from '../Card';
import { Layers } from '../Layers';
import { FormulaWidget } from '../widgets/FormulaWidget';
import { CategoryWidget } from '../widgets/CategoryWidget';
import { useDebouncedState } from '../../hooks';
import { useDebouncedState } from '../../hooks/useDebouncedState';
import { AppContext } from '../../context';

const MAP_VIEW = new MapView({ repeat: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Legend } from '../legends/Legend';
import { Layers } from '../Layers';
import { Card } from '../Card';
import { AppContext } from '../../context';
import { useDebouncedState } from '../../hooks/useDebouncedState';

const MAP_VIEW = new MapView({ repeat: true });
const MAP_STYLE =
Expand All @@ -31,7 +32,7 @@ const POP_COLORS: AccessorFunction<unknown, Color> = colorContinuous({
export default function PopulationView() {
const { accessToken, apiBaseUrl } = useContext(AppContext);
const [attributionHTML, setAttributionHTML] = useState('');
const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);
const [viewState, setViewState] = useDebouncedState(INITIAL_VIEW_STATE, 200);

/****************************************************************************
* Sources (https://deck.gl/docs/api-reference/carto/data-sources)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
WidgetStatus,
numberFormatter,
} from '../../utils';
import { useToggleFilter } from '../../hooks';
import { useToggleFilter } from '../../hooks/useToggleFilter';

const { IN } = FilterType;

Expand Down
144 changes: 0 additions & 144 deletions packages/create-react/src/hooks.ts

This file was deleted.

34 changes: 34 additions & 0 deletions packages/create-react/src/hooks/useDebouncedEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useRef } from 'react';

/**
* A debounced alternative to `useEffect`.
*/
export function useDebouncedEffect(
callback: () => void | (() => void),
delay = 200,
deps: unknown[] = [],
) {
const firstTimeRef = useRef(true);
const clearRef = useRef<(() => void) | void>(undefined);

useEffect(
() => {
if (firstTimeRef.current) {
firstTimeRef.current = false;
return;
}

const timeout = setTimeout(() => {
if (clearRef.current && typeof clearRef.current === 'function') {
clearRef.current();
}
clearRef.current = callback();
}, delay);

return () => clearTimeout(timeout);
},
// TODO: Should 'callback' really be omitted?
// eslint-disable-next-line react-hooks/exhaustive-deps
[delay, ...deps],
);
}
57 changes: 57 additions & 0 deletions packages/create-react/src/hooks/useDebouncedState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState, useEffect, useRef } from 'react';

type Timeout = ReturnType<typeof setTimeout>;

/**
* A debounced alternative to `useState`, API-equivalent except for the
* addition of `delay` as the second parameter to the hook.
*
* Example:
* ```
* const [value, setValue] = useDebouncedState(0, 200);
* ```
*
* When `setValue` is called, state does not change immediately. Instead
* the hook waits (`delay` milliseconds) before flushing changes to state.
* Any additional calls to `setValue` will reset the timer, such that state
* is not updated until `delay` milliseconds have passed since the last
* change.
*/
export function useDebouncedState<T>(
initialValue: T,
delay: number,
): [T, (state: T) => void] {
const [debouncedValue, setDebouncedValue] = useState(initialValue);

// Last value passed, will be assigned to debouncedValue after debounce.
const pendingValueRef = useRef(initialValue);

// Active timeout, if any.
const setValueTimeoutRef = useRef<Timeout | null>(null);

// Component-scoped `setValue` function, with debounce.
const setValueRef = useRef((value: T) => {
if (setValueTimeoutRef.current) {
clearTimeout(setValueTimeoutRef.current);
setValueTimeoutRef.current = null;
}

pendingValueRef.current = value;
setValueTimeoutRef.current = setTimeout(() => {
setDebouncedValue(pendingValueRef.current);
setValueTimeoutRef.current = null;
}, delay);
});

// When component unmounts, cancel any active timeout.
useEffect(() => {
return () => {
if (setValueTimeoutRef.current) {
clearTimeout(setValueTimeoutRef.current);
setValueTimeoutRef.current = null;
}
};
}, []);

return [debouncedValue, setValueRef.current];
}
54 changes: 54 additions & 0 deletions packages/create-react/src/hooks/useToggleFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
addFilter,
Filter,
FilterType,
getFilter,
removeFilter,
} from '@carto/api-client';

export type ToggleFilterProps = {
column: string;
owner: string;
filters?: Record<string, Filter>;
onChange?: (filters: Record<string, Filter>) => void;
};

export function useToggleFilter({
column,
owner,
filters,
onChange,
}: ToggleFilterProps): (category: string) => void {
const { IN } = FilterType;

return function onToggleFilter(category: string): void {
if (!filters || !onChange) return;

const filter = getFilter(filters, { column, type: IN, owner });

let values: string[];

if (!filter) {
values = [category];
} else if ((filter.values as string[]).includes(category)) {
values = (filter.values as string[]).filter(
(v: string) => v !== category,
);
} else {
values = [...(filter.values as string[]), category];
}

if (values.length > 0) {
filters = addFilter(filters, {
column,
type: IN,
owner,
values: values,
});
} else {
filters = removeFilter(filters, { column, owner });
}

onChange({ ...filters });
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
WidgetStatus,
numberFormatter,
} from '../../utils';
import { useToggleFilter } from '../../hooks';
import { useToggleFilter } from '../../hooks/useToggleFilter';
const props = withDefaults(
defineProps<{
Expand Down
File renamed without changes.

0 comments on commit b98b87a

Please sign in to comment.