Skip to content

Feat/compt 30 state storage hooks#3

Closed
a-elkhiraooui-ciscode wants to merge 2 commits intodevelopfrom
feat/COMPT-30-state-storage-hooks
Closed

Feat/compt 30 state storage hooks#3
a-elkhiraooui-ciscode wants to merge 2 commits intodevelopfrom
feat/COMPT-30-state-storage-hooks

Conversation

@a-elkhiraooui-ciscode
Copy link
Copy Markdown

Summary

  • What does this PR change?

Why

  • Why is this change needed?

Checklist

  • Added/updated tests (if behavior changed)
  • npm run lint passes
  • npm run typecheck passes
  • npm test passes
  • npm run build passes
  • Added a changeset (npx changeset) if this affects consumers

Notes

  • Anything reviewers should pay attention to?

Copilot AI review requested due to automatic review settings March 30, 2026 12:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the first batch of “state & storage” hooks to the HooksKit library, including shared storage helpers and accompanying tests/docs, aligning with the project goal of SSR-safe, zero-runtime-deps hooks.

Changes:

  • Introduces useDebounce, useLocalStorage, and useSessionStorage hooks under src/hooks/.
  • Adds storage.ts helper for SSR-guarded JSON read/write and Vitest hook tests.
  • Updates hooks barrel exports, repo instructions, changeset metadata, and Husky pre-commit hook.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/hooks/useSessionStorage.ts New sessionStorage-backed state hook using shared storage helpers
src/hooks/useSessionStorage.test.ts Tests for session storage hook behavior and JSON fallback
src/hooks/useLocalStorage.ts New localStorage-backed state hook using shared storage helpers
src/hooks/useLocalStorage.test.ts Tests for local storage hook behavior and JSON fallback
src/hooks/useDebounce.ts New debounced-value hook
src/hooks/useDebounce.test.ts Tests for debounce timing behavior
src/hooks/storage.ts Shared SSR-guarded JSON read/write helpers for storage hooks
src/hooks/index.ts Exports newly added hooks from the hooks barrel
.husky/pre-commit Simplifies pre-commit hook to run lint-staged
.github/instructions/copilot-instructions.md Updates repo/module guidance to HooksKit + SSR-safe hook requirements
.changeset/COMPT-30-state-storage-hooks.md Declares a minor release for the new hooks

Comment on lines +5 to +17
export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
const storage = typeof window === 'undefined' ? undefined : window.localStorage;

const [storedValue, setStoredValue] = useState<T>(() => {
return readStorageValue(storage, key, initialValue);
});

useEffect(() => {
writeStorageValue(storage, key, storedValue);
}, [key, storedValue, storage]);

return [storedValue, setStoredValue];
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state initializer reads from storage only on the first render. If key changes between renders, the hook will not re-read the new key; instead the effect will write the previous storedValue into the new key. Either document/assume key is stable (and remove key from the write effect deps), or add logic to re-sync storedValue from storage when key changes.

Copilot uses AI. Check for mistakes.
return readStorageValue(storage, key, initialValue);
});

useEffect(() => {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state initializer reads from storage only on the first render. If key changes between renders, the hook will not re-read the new key; instead the effect will write the previous storedValue into the new key. Either document/assume key is stable (and remove key from the write effect deps), or add logic to re-sync storedValue from storage when key changes.

Suggested change
useEffect(() => {
useEffect(() => {
if (!storage) {
return;
}
setStoredValue(readStorageValue(storage, key, initialValue));
}, [initialValue, key, storage]);
useEffect(() => {

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +46
const value = readStorageValue(undefined, 'ssr', 'fallback');
expect(value).toBe('fallback');
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name suggests it's validating the useSessionStorage hook, but the assertion is actually against readStorageValue. Either rename the test to reflect what it's covering, or render the hook with window unavailable to validate the hook-level SSR behavior.

Suggested change
const value = readStorageValue(undefined, 'ssr', 'fallback');
expect(value).toBe('fallback');
const { result } = renderHook(() => useSessionStorage('ssr', 'fallback'));
expect(result.current[0]).toBe('fallback');

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +47
it('returns initial value when window is undefined (SSR guard)', () => {
vi.stubGlobal('window', undefined);

const value = readStorageValue(undefined, 'ssr', 'fallback');
expect(value).toBe('fallback');
});
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name suggests it's validating the useLocalStorage hook, but the assertion is actually against readStorageValue. Either rename the test to reflect what it's covering, or render the hook with window unavailable to validate the hook-level SSR behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +12
const timeoutId = window.setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
window.clearTimeout(timeoutId);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useDebounce directly references window.setTimeout/clearTimeout. The repo guidelines state hooks must guard all window/document access (typeof window === 'undefined'). Consider using the global setTimeout/clearTimeout (or globalThis) and/or short-circuiting when window is unavailable to keep the hook usable outside the browser (SSR/tests/non-DOM runtimes).

Suggested change
const timeoutId = window.setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
window.clearTimeout(timeoutId);
const timeoutId = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timeoutId);

Copilot uses AI. Check for mistakes.
…ooks

- useDebounce<T>(value, delay): returns debounced value, resets timer on value/delay change
- useLocalStorage<T>(key, initial): syncs with localStorage, SSR-safe, JSON serialization
- useSessionStorage<T>(key, initial): same pattern for sessionStorage
- Shared storage.ts helper with readStorageValue/writeStorageValue (SSR guard + parse fallback)
- All three exported from src/hooks/index.ts -> src/index.ts
- Full test coverage: timer reset, JSON sync, parse error fallback, SSR guard
- tsc --noEmit passes, lint passes (0 warnings), 13/13 tests pass
…y pre-commit

- .changeset/COMPT-30-state-storage-hooks.md: minor bump summary for 0.0.1 release
- .github/instructions/copilot-instructions.md: updated to HooksKit package identity,
  real src structure with COMPT-30 hooks marked, COMPT-XX branch naming convention
- .husky/pre-commit: removed deprecated husky v9 shebang lines (breaks in v10)
@a-elkhiraooui-ciscode a-elkhiraooui-ciscode force-pushed the feat/COMPT-30-state-storage-hooks branch from b6ce22a to 7004ad3 Compare March 30, 2026 12:24
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants