ADD: useDebounce hook

This commit is contained in:
Marcos Rodriguez Velez 2024-05-25 12:33:21 -04:00
parent cb3aea1e54
commit 02519bce91
No known key found for this signature in database
GPG Key ID: 6030B2F48CCE86D7
3 changed files with 69 additions and 19 deletions

View File

@ -1,7 +1,13 @@
// https://levelup.gitconnected.com/debounce-in-javascript-improve-your-applications-performance-5b01855e086
const debounce = <T extends (...args: any[]) => void>(func: T, wait: number) => {
// blue_modules/debounce.ts
type DebouncedFunction<T extends (...args: any[]) => void> = {
(this: ThisParameterType<T>, ...args: Parameters<T>): void;
cancel(): void;
};
const debounce = <T extends (...args: any[]) => void>(func: T, wait: number): DebouncedFunction<T> => {
let timeout: NodeJS.Timeout | null;
return function executedFunction(this: ThisParameterType<T>, ...args: Parameters<T>) {
const debouncedFunction = function (this: ThisParameterType<T>, ...args: Parameters<T>) {
const later = () => {
timeout = null;
func.apply(this, args);
@ -11,6 +17,15 @@ const debounce = <T extends (...args: any[]) => void>(func: T, wait: number) =>
}
timeout = setTimeout(later, wait);
};
debouncedFunction.cancel = () => {
if (timeout) {
clearTimeout(timeout);
}
timeout = null;
};
return debouncedFunction as DebouncedFunction<T>;
};
export default debounce;

23
hooks/useDebounce.ts Normal file
View File

@ -0,0 +1,23 @@
import { useState, useEffect } from 'react';
import debounce from '../blue_modules/debounce';
const useDebounce = <T>(value: T, delay: number): T => {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = debounce((val: T) => {
setDebouncedValue(val);
}, delay);
handler(value);
return () => {
handler.cancel();
};
}, [value, delay]);
return debouncedValue;
};
export default useDebounce;

View File

@ -1,6 +1,6 @@
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import dayjs from 'dayjs';
import calendar from 'dayjs/plugin/calendar';
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { FlatList, NativeSyntheticEvent, StyleSheet, View } from 'react-native';
import {
@ -18,6 +18,7 @@ import { useTheme } from '../../components/themes';
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import loc from '../../loc';
import { FiatUnit, FiatUnitSource, FiatUnitType, getFiatRate } from '../../models/fiatUnit';
import useDebounce from '../../hooks/useDebounce';
dayjs.extend(calendar);
@ -26,13 +27,15 @@ const Currency: React.FC = () => {
const [isSavingNewPreferredCurrency, setIsSavingNewPreferredCurrency] = useState(false);
const [selectedCurrency, setSelectedCurrency] = useState<FiatUnitType>(FiatUnit.USD);
const [currencyRate, setCurrencyRate] = useState<CurrencyRate>({ LastUpdated: null, Rate: null });
const [isSearchFocused, setIsSearchFocused] = useState(false);
const { colors } = useTheme();
const { setOptions } = useExtendedNavigation();
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, 300);
const data = useMemo(
() => Object.values(FiatUnit).filter(item => item.endPointKey.toLowerCase().includes(search.toLowerCase())),
[search],
() => Object.values(FiatUnit).filter(item => item.endPointKey.toLowerCase().includes(debouncedSearch.toLowerCase())),
[debouncedSearch],
);
const stylesHook = StyleSheet.create({
@ -64,6 +67,8 @@ const Currency: React.FC = () => {
setOptions({
headerSearchBarOptions: {
onChangeText: (event: NativeSyntheticEvent<{ text: string }>) => setSearch(event.nativeEvent.text),
onFocus: () => setIsSearchFocused(true),
onBlur: () => setIsSearchFocused(false),
},
});
}, [setOptions]);
@ -86,7 +91,7 @@ const Currency: React.FC = () => {
} catch (error: any) {
console.log(error);
presentAlert({
message: error.message ? `${loc.settings.currency_fetch_error}: ${error.message}}` : loc.settings.currency_fetch_error,
message: error.message ? `${loc.settings.currency_fetch_error}: ${error.message}` : loc.settings.currency_fetch_error,
});
} finally {
setIsSavingNewPreferredCurrency(false);
@ -95,6 +100,11 @@ const Currency: React.FC = () => {
/>
);
const selectedCurrencyVisible = useMemo(
() => data.some(item => item.endPointKey === selectedCurrency.endPointKey),
[data, selectedCurrency],
);
return (
<View style={[styles.flex, stylesHook.flex]}>
<FlatList
@ -105,19 +115,21 @@ const Currency: React.FC = () => {
initialNumToRender={30}
renderItem={renderItem}
/>
<BlueCard>
<BlueText>
{loc.settings.currency_source} {selectedCurrency?.source ?? FiatUnitSource.CoinDesk}
</BlueText>
<BlueSpacing10 />
<BlueText>
{loc.settings.rate}: {currencyRate.Rate ?? loc._.never}
</BlueText>
<BlueSpacing10 />
<BlueText>
{loc.settings.last_updated}: {dayjs(currencyRate.LastUpdated).calendar() ?? loc._.never}
</BlueText>
</BlueCard>
{!isSearchFocused || selectedCurrencyVisible ? (
<BlueCard>
<BlueText>
{loc.settings.currency_source} {selectedCurrency?.source ?? FiatUnitSource.CoinDesk}
</BlueText>
<BlueSpacing10 />
<BlueText>
{loc.settings.rate}: {currencyRate.Rate ?? loc._.never}
</BlueText>
<BlueSpacing10 />
<BlueText>
{loc.settings.last_updated}: {dayjs(currencyRate.LastUpdated).calendar() ?? loc._.never}
</BlueText>
</BlueCard>
) : null}
</View>
);
};