Skip to content

Feat/compt 30 state storage hooks#6

Merged
a-elkhiraooui-ciscode merged 2 commits intodevelopfrom
feat/COMPT-30-state-storage-hooks
Mar 30, 2026
Merged

Feat/compt 30 state storage hooks#6
a-elkhiraooui-ciscode merged 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?

…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)
Copilot AI review requested due to automatic review settings March 30, 2026 12:45
@sonarqubecloud
Copy link
Copy Markdown

@a-elkhiraooui-ciscode a-elkhiraooui-ciscode merged commit 8cde8b0 into develop Mar 30, 2026
5 of 6 checks passed
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 “state & storage” hooks for the HooksKit library, plus supporting storage utilities, tests, and documentation/metadata updates.

Changes:

  • Introduces useDebounce, useLocalStorage, and useSessionStorage hooks with Vitest coverage.
  • Adds shared storage.ts helpers for JSON (de)serialization with SSR guards.
  • Updates hook barrel exports, Changeset metadata, Husky hook config, and contributor instructions.

Reviewed changes

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

Show a summary per file
File Description
src/hooks/useSessionStorage.ts New hook for sessionStorage-backed state using shared storage helpers.
src/hooks/useSessionStorage.test.ts Tests for session storage behavior and fallback scenarios.
src/hooks/useLocalStorage.ts New hook for localStorage-backed state using shared storage helpers.
src/hooks/useLocalStorage.test.ts Tests for local storage behavior and fallback scenarios.
src/hooks/useDebounce.ts New debounce hook implemented with a timeout-based effect.
src/hooks/useDebounce.test.ts Tests covering debounce timing behavior with fake timers.
src/hooks/storage.ts New shared helpers to read/write JSON values with SSR/error guards.
src/hooks/index.ts Exports the newly added hooks from the hooks barrel.
.husky/pre-commit Modifies the pre-commit hook to run lint-staged.
.github/instructions/copilot-instructions.md Updates project guidelines/docs for the new HooksKit focus.
.changeset/COMPT-30-state-storage-hooks.md Adds a Changeset entry describing the new hooks release.

@@ -0,0 +1,21 @@
---
"@ciscode/reactts-developerkit": minor
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 changeset frontmatter targets "@ciscode/reactts-developerkit", but package.json declares the package name as @ciscode/hooks-kit. With the wrong package name, Changesets won’t version/release the actual package. Update the changeset to reference the real package name.

Suggested change
"@ciscode/reactts-developerkit": minor
"@ciscode/hooks-kit": minor

Copilot uses AI. Check for mistakes.

**Package**: `@ciscode/ui-components` (example)
**Type**: React Component Library
**Package**: `@ciscode/reactts-developerkit`
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 doc states the package name is @ciscode/reactts-developerkit, but package.json currently uses @ciscode/hooks-kit. Please align the documentation with the actual published package name to avoid confusing consumers/contributors.

Suggested change
**Package**: `@ciscode/reactts-developerkit`
**Package**: `@ciscode/hooks-kit`

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +14
writeStorageValue(storage, key, storedValue);
}, [key, storedValue, storage]);
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.

When key changes, the hook keeps the previous storedValue (since the useState initializer only runs once) but the effect will start writing that old value under the new key. This can overwrite an existing value for the new key and never re-read it. Either document that key must be stable, or add logic to re-read/reset state when key changes (and avoid overwriting until after the re-read).

Suggested change
writeStorageValue(storage, key, storedValue);
}, [key, storedValue, storage]);
const value = readStorageValue(storage, key, initialValue);
setStoredValue(value);
}, [key, storage, initialValue]);
useEffect(() => {
writeStorageValue(storage, key, storedValue);
}, [storedValue, storage]);

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +17
const [storedValue, setStoredValue] = useState<T>(() => {
return readStorageValue(storage, key, initialValue);
});

useEffect(() => {
writeStorageValue(storage, key, storedValue);
}, [key, storedValue, storage]);
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.

If key changes over time, the hook won’t re-read sessionStorage for the new key (the useState initializer runs once), but the effect will write the existing state to the new key. That can overwrite a pre-existing value for the new key. Consider either enforcing/documenting a stable key, or syncing state on key changes before writing.

Copilot uses AI. Check for mistakes.
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
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.

.husky/pre-commit no longer has a shebang or Husky bootstrap (. "$(dirname -- "$0")/_/husky.sh"). Git executes hooks as standalone programs; without a shebang this commonly fails with an “exec format error” and the hook won’t run. Restore the standard Husky header so the hook is executable and runs reliably across environments.

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 uses window.setTimeout/window.clearTimeout. This breaks the repo’s SSR-safety convention (all window access must be guarded) and also fails in non-browser React environments where window is undefined. Prefer globalThis.setTimeout/globalThis.clearTimeout or the unqualified setTimeout/clearTimeout (and/or guard with typeof window === 'undefined'). See .github/instructions/copilot-instructions.md (SSR-safe rule).

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.
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 is labeled as verifying the hook’s SSR guard, but it never calls useLocalStorage; it only tests readStorageValue. That leaves useLocalStorage’s SSR behavior untested and can let regressions slip through. Either rename/move this test to a storage helper test suite, or add a test that renders useLocalStorage with window undefined and asserts it returns the initial value without throwing.

Copilot generated this review using guidance from repository custom instructions.
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 is labeled as verifying the hook’s SSR guard, but it never calls useSessionStorage; it only tests readStorageValue. That means useSessionStorage’s SSR behavior isn’t actually covered. Either rename/move this test to a storage helper test suite, or add a test that renders useSessionStorage with window undefined and asserts it returns the initial value without throwing.

Copilot generated this review using guidance from repository custom instructions.
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