Skip to content

Instantly share code, notes, and snippets.

@andreialecu
Created May 15, 2024 08:32
Show Gist options
  • Save andreialecu/4728d047233005be1ef9262c01ac94ef to your computer and use it in GitHub Desktop.
Save andreialecu/4728d047233005be1ef9262c01ac94ef to your computer and use it in GitHub Desktop.
use-number-formatter hook for react native
import {useCallback} from 'react';
import {millify} from '../millify';
import {useAppLanguage} from './use-app-language';
const cachedFormatters = new Map<string, Intl.NumberFormat>();
const cachedResults = new Map<Intl.NumberFormat, Map<number, string>>();
/**
* We cache Intl.NumberFormat instances because creating them is slow.
* On Android this speeds up this code path about 10x.
*/
function getCachedFormatter({
language,
opts,
}: // notation,
{
language: string;
opts?: Intl.NumberFormatOptions;
}) {
const key = [language, JSON.stringify(opts)].join(':');
if (!cachedFormatters.has(key)) {
try {
cachedFormatters.set(
key,
Intl.NumberFormat(language, {
...opts,
// don't use more than 20 digits, otherwise this errors
// needed because mocks can generate a number that is out of range
minimumFractionDigits: Math.min(opts?.minimumFractionDigits || 0, 20),
// cannot use compact because of: https://github.com/facebook/hermes/issues/1190
// notation,
}),
);
} catch (error) {
console.error('Error creating number formatter', {language, opts});
cachedFormatters.set(
key,
Intl.NumberFormat('en-GB', {
// don't use more than 20 digits, otherwise this errors
// needed because mocks can generate a number that is out of range
minimumFractionDigits: Math.min(opts?.minimumFractionDigits || 0, 20),
// cannot use compact because of: https://github.com/facebook/hermes/issues/1190
// notation,
}),
);
}
}
const formatter = cachedFormatters.get(key)!;
return {
format: (n: number) => {
if (!cachedResults.has(formatter)) {
cachedResults.set(formatter, new Map());
}
const results = cachedResults.get(formatter)!;
if (!results.has(n)) {
results.set(n, formatter.format(n));
}
return results.get(n)!;
},
};
}
export function useNumberFormatter() {
const appLanguage = useAppLanguage();
return useCallback(
(
n: number | null | undefined,
{notation, ...opts}: Intl.NumberFormatOptions = {},
) => {
let num = n || 0;
const formatter = getCachedFormatter({
language: appLanguage,
opts,
});
// default to compact
if (
(!notation || notation === 'compact') &&
// only if we can round to 1 digit
num % 100 === 0
) {
return millify(num, formatter);
}
return formatter.format(num);
},
[appLanguage],
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment