diff --git a/.changeset/dirty-coats-fold.md b/.changeset/dirty-coats-fold.md new file mode 100644 index 00000000..bbe7d7c2 --- /dev/null +++ b/.changeset/dirty-coats-fold.md @@ -0,0 +1,5 @@ +--- +'react-simplikit': patch +--- + +feat(core/hooks): add 'useIsClient' hook diff --git a/packages/core/src/hooks/useIsClient/index.ts b/packages/core/src/hooks/useIsClient/index.ts new file mode 100644 index 00000000..b8d97825 --- /dev/null +++ b/packages/core/src/hooks/useIsClient/index.ts @@ -0,0 +1 @@ +export { useIsClient } from './useIsClient.ts'; diff --git a/packages/core/src/hooks/useIsClient/ko/useIsClient.md b/packages/core/src/hooks/useIsClient/ko/useIsClient.md new file mode 100644 index 00000000..34eef9a5 --- /dev/null +++ b/packages/core/src/hooks/useIsClient/ko/useIsClient.md @@ -0,0 +1,33 @@ +# useIsClient + +`useIsClient`는 클라이언트 환경에서만 `true`를 반환하는 리액트 훅이에요. 주로 클라이언트와 서버 사이드 렌더링(SSR)을 구분하기 위해 사용돼요. 컴포넌트가 클라이언트 환경에서 마운트된 후에만 `true`로 설정돼요. + +## 인터페이스 + +```ts +function useIsClient(): boolean; +``` + +### 반환 값 + + + +## 예시 + +```tsx +import { useIsClient } from 'react-simplikit'; + +function ClientSideContent() { + const isClient = useIsClient(); + + if (!isClient) { + return
Loading...
; + } + + return
Client-side rendered content
; +} +``` diff --git a/packages/core/src/hooks/useIsClient/useIsClient.md b/packages/core/src/hooks/useIsClient/useIsClient.md new file mode 100644 index 00000000..561bc22f --- /dev/null +++ b/packages/core/src/hooks/useIsClient/useIsClient.md @@ -0,0 +1,33 @@ +# useIsClient + +`useIsClient` is a React hook that returns `true` only in the client-side environment. It is primarily used to differentiate between client-side and server-side rendering (SSR). The state is set to `true` only after the component is mounted in the client-side environment. + +## Interface + +```ts +function useIsClient(): boolean; +``` + +### Return Value + + + +## Example + +```tsx +import { useIsClient } from 'react-simplikit'; + +function ClientSideContent() { + const isClient = useIsClient(); + + if (!isClient) { + return
Loading...
; + } + + return
Client-side rendered content
; +} +``` diff --git a/packages/core/src/hooks/useIsClient/useIsClient.spec.ts b/packages/core/src/hooks/useIsClient/useIsClient.spec.ts new file mode 100644 index 00000000..ad3245cc --- /dev/null +++ b/packages/core/src/hooks/useIsClient/useIsClient.spec.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; + +import { renderHookSSR } from '../../_internal/test-utils/renderHookSSR.tsx'; + +import { useIsClient } from './useIsClient.ts'; + +describe('useIsClient', () => { + it('is safe on server side rendering', async () => { + const result = renderHookSSR.serverOnly(() => useIsClient()); + + expect(result.current).toBe(false); + }); + + it('should return true after hydration on the client', async () => { + const { result } = await renderHookSSR(() => useIsClient()); + + expect(result.current).toBe(true); + }); +}); diff --git a/packages/core/src/hooks/useIsClient/useIsClient.ts b/packages/core/src/hooks/useIsClient/useIsClient.ts new file mode 100644 index 00000000..ea350e78 --- /dev/null +++ b/packages/core/src/hooks/useIsClient/useIsClient.ts @@ -0,0 +1,48 @@ +import { useEffect, useState } from 'react'; + +/** + * @description + * `useIsClient` is a React hook that returns `true` only in the client-side environment. + * It is primarily used to differentiate between client-side and server-side rendering (SSR). + * The state is set to `true` only after the component is mounted in the client-side environment. + * + * @returns {boolean} Returns `true` in a client-side environment, and `false` otherwise. + * + * @example + * function ClientSideContent() { + * const isClient = useIsClient(); + * + * if (!isClient) { + * return
Loading...
; // Rendered on the server side + * } + * + * return
Client-side rendered content
; // Rendered on the client side + * } + * + * @example + * function ClientOnlyMap() { + * const isClient = useIsClient(); + * + * if (!isClient) return null; + * + * return
; + * } + * + * @example + * function ClientTheme() { + * const isClient = useIsClient(); + * + * const theme = isClient ? localStorage.getItem('theme') : 'light'; + * + * return
Current theme: {theme}
; + * } + */ +export function useIsClient() { + const [isClient, setIsClient] = useState(false); + + useEffect(function syncClientState() { + setIsClient(true); + }, []); + + return isClient; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 111f1168..975dd899 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,6 +15,7 @@ export { useImpressionRef } from './hooks/useImpressionRef/index.ts'; export { useInputState } from './hooks/useInputState/index.ts'; export { useIntersectionObserver } from './hooks/useIntersectionObserver/index.ts'; export { useInterval } from './hooks/useInterval/index.ts'; +export { useIsClient } from './hooks/useIsClient/index.ts'; export { useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect/index.ts'; export { useLoading } from './hooks/useLoading/index.ts'; export { useLongPress } from './hooks/useLongPress/index.ts';