Skip to content

Feat/compt 30 state storage hooks#1

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

Feat/compt 30 state storage hooks#1
a-elkhiraooui-ciscode wants to merge 2 commits intomasterfrom
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)
@sonarqubecloud
Copy link
Copy Markdown

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 to the HooksKit package, along with shared storage helpers, tests, and public exports to expose the new hooks as part of the library API.

Changes:

  • Introduces useDebounce, useLocalStorage, and useSessionStorage hooks under src/hooks/.
  • Adds shared SSR-guarded JSON storage helpers (readStorageValue / writeStorageValue) and Vitest coverage for the new hooks.
  • Updates hook barrel exports, Copilot instructions, Husky pre-commit hook, and includes a changeset for a minor release.

Reviewed changes

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

Show a summary per file
File Description
src/hooks/useSessionStorage.ts Adds sessionStorage-backed state hook using shared storage helpers.
src/hooks/useSessionStorage.test.ts Adds tests for session storage behavior + SSR helper guard.
src/hooks/useLocalStorage.ts Adds localStorage-backed state hook using shared storage helpers.
src/hooks/useLocalStorage.test.ts Adds tests for local storage behavior + SSR helper guard.
src/hooks/useDebounce.ts Adds value debouncing hook using a timeout.
src/hooks/useDebounce.test.ts Adds fake-timer tests for debounce timing behavior.
src/hooks/storage.ts Introduces shared JSON read/write helpers with SSR guard and error swallowing.
src/hooks/index.ts Exports the new hooks from the hooks barrel.
.husky/pre-commit Adjusts pre-commit hook command to run lint-staged.
.github/instructions/copilot-instructions.md Updates repo/module guidance to “HooksKit” and documents SSR-safe expectations.
.changeset/COMPT-30-state-storage-hooks.md Declares a minor release for adding the new hooks.

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 will throw in non-DOM runtimes (e.g., React Native/tests without JSDOM) and contradicts the repo’s stated SSR-safety goal. Prefer globalThis.setTimeout/globalThis.clearTimeout (or the unqualified setTimeout/clearTimeout) so the hook doesn’t require window.

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 1 to 3
// Example placeholder export — replace with real hooks later.
export const __hooks_placeholder = true;

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.

__hooks_placeholder is exported from the hooks barrel and therefore becomes part of the package’s public API via src/index.ts. Now that real hooks are present, this placeholder export should be removed to avoid shipping a permanent, undocumented API surface.

Suggested change
// Example placeholder export — replace with real hooks later.
export const __hooks_placeholder = true;

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +11
if (typeof window !== 'undefined') {
window.localStorage.clear();
}
vi.unstubAllGlobals();
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.

afterEach clears localStorage before calling vi.unstubAllGlobals(). If a test stubs window to undefined, the clear is skipped and storage can leak into later tests. Unstub/restore globals first (or use a finally) and then clear storage to keep cleanup reliable regardless of test order.

Suggested change
if (typeof window !== 'undefined') {
window.localStorage.clear();
}
vi.unstubAllGlobals();
vi.unstubAllGlobals();
if (typeof window !== 'undefined') {
window.localStorage.clear();
}

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +11
if (typeof window !== 'undefined') {
window.sessionStorage.clear();
}
vi.unstubAllGlobals();
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.

afterEach clears sessionStorage before calling vi.unstubAllGlobals(). If a test stubs window to undefined, the clear is skipped and storage can leak into later tests. Unstub/restore globals first (or use a finally) and then clear storage to keep cleanup reliable regardless of test order.

Suggested change
if (typeof window !== 'undefined') {
window.sessionStorage.clear();
}
vi.unstubAllGlobals();
vi.unstubAllGlobals();
if (typeof window !== 'undefined') {
window.sessionStorage.clear();
}

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
const storage = typeof window === 'undefined' ? undefined : window.localStorage;
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.

Accessing window.localStorage can throw (e.g., in some privacy modes or sandboxed iframes). Since this happens during render, it would crash the hook before the try/catch in readStorageValue/writeStorageValue can help. Consider wrapping the storage acquisition in a try/catch (returning undefined on failure) or centralizing it in a safe helper.

Suggested change
export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
const storage = typeof window === 'undefined' ? undefined : window.localStorage;
function getLocalStorage(): Storage | undefined {
if (typeof window === 'undefined') {
return undefined;
}
try {
return window.localStorage;
} catch {
return undefined;
}
}
export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
const storage = getLocalStorage();

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +9
export function useSessionStorage<T>(
key: string,
initialValue: T,
): [T, Dispatch<SetStateAction<T>>] {
const storage = typeof window === 'undefined' ? undefined : window.sessionStorage;
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.

Accessing window.sessionStorage can throw (e.g., in some privacy modes or sandboxed iframes). Since this happens during render, it would crash the hook before the try/catch in readStorageValue/writeStorageValue can help. Consider wrapping the storage acquisition in a try/catch (returning undefined on failure) or centralizing it in a safe helper.

Suggested change
export function useSessionStorage<T>(
key: string,
initialValue: T,
): [T, Dispatch<SetStateAction<T>>] {
const storage = typeof window === 'undefined' ? undefined : window.sessionStorage;
function getSessionStorage(): Storage | undefined {
if (typeof window === 'undefined') {
return undefined;
}
try {
return window.sessionStorage;
} catch {
return undefined;
}
}
export function useSessionStorage<T>(
key: string,
initialValue: T,
): [T, Dispatch<SetStateAction<T>>] {
const storage = getSessionStorage();

Copilot uses AI. Check for mistakes.
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