Skip to content

Commit

Permalink
fix(pixels): add hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
toxsick committed Apr 12, 2024
1 parent af60655 commit 4ab0414
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/pixels/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
]
}
},
"files": [
"./dist"
],
"homepage": "https://github.com/fuf-stack/uniforms#readme",
"license": "MIT",
"publishConfig": {
Expand Down
22 changes: 22 additions & 0 deletions packages/pixels/src/hooks/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect, useState } from 'react';

export default <Value>(value: Value, delay: number) => {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay], // Only re-call effect if value or delay changes
);
return debouncedValue;
};
83 changes: 83 additions & 0 deletions packages/pixels/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Dispatch, SetStateAction } from 'react';

import { useEffect, useState } from 'react';

const useLocalStorage = <T>(
key: string,
initialValue: T | (() => T),
): [T, Dispatch<SetStateAction<T>>] => {
// Get from local storage then
// parse stored json or return initialValue
const readValue = () => {
// Prevent build error "window is undefined" but keep keep working
if (typeof window === 'undefined') {
return initialValue;
}

try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.warn(`Error reading localStorage key “${key}”:`, error);
return initialValue;
}
};

// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState<T>(readValue);

// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue: Dispatch<SetStateAction<T>> = (value) => {
// Prevent build error "window is undefined" but keeps working
if (typeof window === 'undefined') {
console.warn(
`Tried setting localStorage key “${key}” even though environment is not a client`,
);
}

try {
// Allow value to be a function so we have the same API as useState
const newValue = value instanceof Function ? value(storedValue) : value;

// Save to local storage
window.localStorage.setItem(key, JSON.stringify(newValue));

// Save state
setStoredValue(newValue);

// We dispatch a custom event so every useLocalStorage hook are notified
window.dispatchEvent(new Event('local-storage'));
} catch (error) {
console.warn(`Error setting localStorage key “${key}”:`, error);
}
};

useEffect(() => {
setStoredValue(readValue());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const handleStorageChange = () => {
setStoredValue(readValue());
};

// this only works for other documents, not the current one
window.addEventListener('storage', handleStorageChange);

// this is a custom event, triggered in writeValueToLocalStorage
window.addEventListener('local-storage', handleStorageChange);

return () => {
window.removeEventListener('storage', handleStorageChange);
window.removeEventListener('local-storage', handleStorageChange);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return [storedValue, setValue];
};

export default useLocalStorage;

0 comments on commit 4ab0414

Please sign in to comment.