Skip to content

Commit

Permalink
refac(react): storage setState 타입 변경 (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssi02014 committed Sep 19, 2024
1 parent bc86b54 commit e150f81
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-rats-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/react': patch
---

refac(react): storage setState 타입 변경 - @ssi02014
4 changes: 2 additions & 2 deletions docs/docs/react/hooks/useLocalStorage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ function useLocalStorage<T>({
initialValue,
}: UseLocalStorageWithInitialValueProps<T>): {
state: T;
setState: (value: T | ((state: T) => T)) => void;
setState: Dispatch<SetStateAction<T>>;
removeState: () => void;
};

function useLocalStorage<T = unknown>({
key,
}: UseLocalStorageWithoutInitialValueProps): {
state: T | null;
setState: (value: T | ((state: T | null) => T)) => void;
setState: Dispatch<SetStateAction<T | null>>;
removeState: () => void;
};
```
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/react/hooks/useSessionStorage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ function useSessionStorage<T>({
initialValue,
}: UseSessionStorageWithInitialValueProps<T>): {
state: T;
setState: (value: T | ((state: T) => T)) => void;
setState: Dispatch<SetStateAction<T>>;
removeState: () => void;
};

function useSessionStorage<T = unknown>({
key,
}: UseSessionStorageWithoutInitialValueProps): {
state: T | null;
setState: (value: T | ((state: T | null) => T)) => void;
setState: Dispatch<SetStateAction<T | null>>;
removeState: () => void;
};
```
Expand Down
17 changes: 16 additions & 1 deletion packages/react/src/hooks/useDocumentTitle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ interface UseDocumentTitleOption {
preserveTitleOnUnmount?: boolean;
}

/**
* @description Client 환경에서 문서의 제목(`document.title`)을 설정하는 커스텀 훅입니다.
*
* `useDocumentTitle` 훅은 컴포넌트가 마운트될 때 주어진 제목으로 `document.title`을 설정하고,
* 컴포넌트가 언마운트될 때 선택적으로 이전 제목으로 복원할 수 있습니다.
*
* @param {string} title - 설정할 문서의 제목입니다.
* @param {{ preserveTitleOnUnmount: boolean }} options - 옵션 객체입니다.
* - `preserveTitleOnUnmount`: `true`로 설정하면 컴포넌트가 언마운트될 때 제목을 이전 상태로 복원하지 않습니다. 기본값은 `false`입니다.
*
* @returns {void}
*
* @example
* useDocumentTitle('새로운 제목', { preserveTitleOnUnmount: true });
*/
export function useDocumentTitle(
title: string,
{ preserveTitleOnUnmount = false }: UseDocumentTitleOption = {}
) {
): void {
useIsomorphicLayoutEffect(() => {
const prevTitle = document.title;
document.title = title;
Expand Down
11 changes: 10 additions & 1 deletion packages/react/src/hooks/useForceUpdate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ import { useReducer } from 'react';

const updateReducer = (num: number) => (num + 1) % 10000000;

export function useForceUpdate() {
/**
* @description 컴포넌트의 리렌더링을 강제하는 커스텀 훅입니다.
* 이 훅은 `forceUpdate` 함수를 제공하며, 이 함수를 호출하면 컴포넌트를 강제로 리렌더링합니다.
*
* @returns {() => void} - 호출 시 컴포넌트를 강제로 리렌더링하는 함수입니다.
*
* @example
* const forceUpdate = useForceUpdate();
*/
export function useForceUpdate(): () => void {
const [, forceUpdate] = useReducer(updateReducer, 0);

return forceUpdate;
Expand Down
14 changes: 10 additions & 4 deletions packages/react/src/hooks/useLocalStorage/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { isFunction, parseJSON } from '@modern-kit/utils';
import { useCallback, useMemo, useSyncExternalStore } from 'react';
import {
Dispatch,
SetStateAction,
useCallback,
useMemo,
useSyncExternalStore,
} from 'react';
import { usePreservedState } from '../usePreservedState';
import {
getServerSnapshot,
Expand Down Expand Up @@ -27,15 +33,15 @@ export function useLocalStorage<T>({
initialValue,
}: UseLocalStorageWithInitialValueProps<T>): {
state: T;
setState: (value: T | ((state: T) => T)) => void;
setState: Dispatch<SetStateAction<T>>;
removeState: () => void;
};

export function useLocalStorage<T = unknown>({
key,
}: UseLocalStorageWithoutInitialValueProps): {
state: T | null;
setState: (value: T | ((state: T | null) => T)) => void;
setState: Dispatch<SetStateAction<T | null>>;
removeState: () => void;
};

Expand All @@ -60,7 +66,7 @@ export function useLocalStorage<T>(props: UseLocalStorageProps<T>) {
}, [externalStoreState, initialValueToUse]);

const setState = useCallback(
(value: T | ((state: T | null) => T)) => {
(value: Dispatch<SetStateAction<T | null>>) => {
try {
const prevStateString = getSnapshot(key);
const prevState = prevStateString
Expand Down
30 changes: 27 additions & 3 deletions packages/react/src/hooks/useOnClickOutside/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@ import { useEffect, useRef } from 'react';
import { usePreservedCallback } from '../usePreservedCallback';
import { isMobile } from '@modern-kit/utils';

/**
* @description 특정 요소 외부에서 마우스 또는 터치 이벤트가 발생할 때 호출되는 콜백을 등록하는 커스텀 훅입니다.
*
* `useOnClickOutside` 훅은 지정된 요소(ref로 지정된 요소) 외부에서 사용자가 마우스를 클릭하거나
* 터치 이벤트가 발생할 때마다 제공된 `callback` 함수를 호출합니다. 모바일 환경에서는 `touchstart` 이벤트를,
* 데스크탑 환경에서는 `mousedown` 이벤트를 감지합니다.
*
* @template T - HTML 요소의 타입. 기본적으로 `HTMLElement`를 상속합니다.
* @param {(targetElement: T) => void} callback - 요소 외부에서 포인터 다운 이벤트가 발생할 때 호출되는 콜백 함수입니다.
* 해당 요소의 레퍼런스를 매개변수로 받습니다.
*
* @returns {{ ref: React.RefObject<T> }} - 외부 클릭 감지를 원하는 DOM 요소에 연결할 ref 객체를 반환합니다.
*
* @example
* ```tsx
* const targetRef = useOnClickOutside<HTMLDivElement>((targetElement) => {
* console.log('외부 클릭 감지:', targetElement);
* });
*
* <div className='outside-box'>
* <div ref={targetRef} className='inner-box' />
* </div>
* ```
*/
export function useOnClickOutside<T extends HTMLElement>(
action: (targetElement: T) => void
) {
callback: (targetElement: T) => void
): { ref: React.RefObject<T> } {
const ref = useRef<T>(null);
const callbackAction = usePreservedCallback(action);
const callbackAction = usePreservedCallback(callback);

useEffect(() => {
const eventType = isMobile() ? 'touchstart' : 'mousedown';
Expand Down
14 changes: 10 additions & 4 deletions packages/react/src/hooks/useSessionStorage/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { isFunction, parseJSON } from '@modern-kit/utils';
import { useCallback, useMemo, useSyncExternalStore } from 'react';
import {
Dispatch,
SetStateAction,
useCallback,
useMemo,
useSyncExternalStore,
} from 'react';
import { usePreservedState } from '../usePreservedState';
import {
getServerSnapshot,
Expand All @@ -26,15 +32,15 @@ export function useSessionStorage<T>({
initialValue,
}: UseSessionStorageWithInitialValueProps<T>): {
state: T;
setState: (value: T | ((state: T) => T)) => void;
setState: Dispatch<SetStateAction<T>>;
removeState: () => void;
};

export function useSessionStorage<T = unknown>({
key,
}: UseSessionStorageWithoutInitialValueProps): {
state: T | null;
setState: (value: T | ((state: T | null) => T)) => void;
setState: Dispatch<SetStateAction<T | null>>;
removeState: () => void;
};

Expand All @@ -59,7 +65,7 @@ export function useSessionStorage<T>(props: UseSessionStorageProps<T>) {
}, [externalStoreState, initialValueToUse]);

const setState = useCallback(
(value: T | ((state: T | null) => T)) => {
(value: Dispatch<SetStateAction<T | null>>) => {
try {
const prevStateString = getSnapshot(key);
const prevState = prevStateString
Expand Down
17 changes: 16 additions & 1 deletion packages/utils/src/device/isMobile/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import { isServer } from '../../device/isServer';

export function isMobile() {
/**
* @description 현재 장치가 모바일 장치인지 여부를 확인하는 함수입니다.
*
* `isMobile` 함수는 브라우저의 `userAgent` 문자열을 사용하여 현재 사용자가
* 모바일 장치를 사용 중인지 판별합니다. 만약 서버 환경에서 호출될 경우, 항상 `false`를 반환합니다.
*
* @returns {boolean} - 모바일 장치라면 `true`, 그렇지 않다면 `false`를 반환합니다.
*
* @example
* if (isMobile()) {
* console.log('모바일 장치입니다.');
* } else {
* console.log('모바일 장치가 아닙니다.');
* }
*/
export function isMobile(): boolean {
if (isServer()) return false;

const userAgent = window.navigator.userAgent;
Expand Down

0 comments on commit e150f81

Please sign in to comment.