@react-hookz/web
Version:
React hooks done right, for browser and SSR.
97 lines (96 loc) • 3.46 kB
JavaScript
/* eslint-disable @typescript-eslint/no-use-before-define,no-use-before-define */
import { useCallback, useEffect } from 'react';
import * as Cookies from 'js-cookie';
import { useFirstMountState, useMountEffect, useSafeState, useSyncedRef } from '..';
import { isBrowser } from "../util/const.js";
const cookiesSetters = new Map();
const registerSetter = (key, setter) => {
let setters = cookiesSetters.get(key);
if (!setters) {
setters = new Set();
cookiesSetters.set(key, setters);
}
setters.add(setter);
};
const unregisterSetter = (key, setter) => {
const setters = cookiesSetters.get(key);
// almost impossible to test in normal situation
/* istanbul ignore next */
if (!setters)
return;
setters.delete(setter);
if (!setters.size) {
cookiesSetters.delete(key);
}
};
const invokeRegisteredSetters = (key, value, skipSetter) => {
const setters = cookiesSetters.get(key);
// almost impossible to test in normal situation
/* istanbul ignore next */
if (!setters)
return;
setters.forEach((s) => {
if (s !== skipSetter)
s(value);
});
};
/**
* Manages a single cookie.
*
* @param key Cookie name to manage.
* @param options Cookie options that will be used during cookie set and delete.
*/
export function useCookieValue(key, options = {}) {
// no need to test it, dev-only notification about 3rd party library requirement
/* istanbul ignore next */
if (process.env.NODE_ENV === 'development' && typeof Cookies === 'undefined') {
throw new ReferenceError('Dependency `js-cookies` is not installed, it is required for `useCookieValue` work.');
}
// eslint-disable-next-line prefer-const
let { initializeWithValue = true, ...cookiesOptions } = options;
if (!isBrowser) {
initializeWithValue = false;
}
const methods = useSyncedRef({
set: (value) => {
setState(value);
Cookies.set(key, value, cookiesOptions);
// update all other hooks with the same key
invokeRegisteredSetters(key, value, setState);
},
remove: () => {
setState(null);
Cookies.remove(key, cookiesOptions);
invokeRegisteredSetters(key, null, setState);
},
fetchVal: () => Cookies.get(key) ?? null,
fetch: () => {
const val = methods.current.fetchVal();
setState(val);
invokeRegisteredSetters(key, val, setState);
},
});
const isFirstMount = useFirstMountState();
const [state, setState] = useSafeState(isFirstMount && initializeWithValue ? methods.current.fetchVal() : undefined);
useMountEffect(() => {
if (!initializeWithValue) {
methods.current.fetch();
}
});
useEffect(() => {
registerSetter(key, setState);
return () => {
unregisterSetter(key, setState);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]);
return [
state,
// eslint-disable-next-line react-hooks/exhaustive-deps
useCallback((value) => methods.current.set(value), []),
// eslint-disable-next-line react-hooks/exhaustive-deps
useCallback(() => methods.current.remove(), []),
// eslint-disable-next-line react-hooks/exhaustive-deps
useCallback(() => methods.current.fetch(), []),
];
}