From 84b0ddc137ee548b5f080b6b87dcb0c3546e538a Mon Sep 17 00:00:00 2001 From: ssi02014 Date: Wed, 1 May 2024 00:07:39 +0900 Subject: [PATCH] =?UTF-8?q?docs(react):=20useBlockPromiseMultipleClick=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/great-parents-approve.md | 5 + .../hooks/useBlockPromiseMultipleClick.mdx | 99 +++++++++++++++++++ packages/react/src/hooks/index.ts | 2 +- .../useBlockPromiseMultipleClick/index.ts | 2 +- .../useBlockPromiseMultipleClick.spec.tsx | 10 +- 5 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 .changeset/great-parents-approve.md create mode 100644 docs/docs/react/hooks/useBlockPromiseMultipleClick.mdx diff --git a/.changeset/great-parents-approve.md b/.changeset/great-parents-approve.md new file mode 100644 index 000000000..e47d5d2c6 --- /dev/null +++ b/.changeset/great-parents-approve.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/react': minor +--- + +feat(react): useBlockDoubleClick 추가 @Sangminnn diff --git a/docs/docs/react/hooks/useBlockPromiseMultipleClick.mdx b/docs/docs/react/hooks/useBlockPromiseMultipleClick.mdx new file mode 100644 index 000000000..f47aa6286 --- /dev/null +++ b/docs/docs/react/hooks/useBlockPromiseMultipleClick.mdx @@ -0,0 +1,99 @@ +import { useState } from 'react'; +import { useBlockPromiseMultipleClick } from '@modern-kit/react'; + +# useBlockPromiseMultipleClick + +인자로 넘겨준 Callback 함수의 `Promise` 동작을 수행하는 동안 `중복 호출이 불가능하도록 차단`하는 커스텀 훅입니다. + +[useDebounce](https://modern-agile-team.github.io/modern-kit/docs/react/hooks/useDebounce)를 사용해 중복 호출을 방지할 수는 있지만, 시간 값에 의존하기 때문에 만약 `Promise`가 이행될 때까지 호출을 완벽하게 차단해야 한다면 부족합니다. + +해당 훅은 Promise 동작을 수행하는 동안 중복 호출을 방지하기 때문에, `Promise` 이행을 보장하면서 중복 호출을 방지하고 싶을 때 사용할 수 있습니다. + +
+ +## Interface +```tsx +const useBlockPromiseMultipleClick: () => { + isLoading: boolean; + blockMultipleClick: (callback: () => Promise) => Promise; +}; +``` + +## Usage + +```tsx +import React, { useState } from 'react'; +import { useBlockPromiseMultipleClick } from '@modern-kit/react'; + +interface Value { + userId: number; + id: number; + title: string; + completed: boolean; +} + +const Example = () => { + const [blockingCount, setBlockingCount] = useState(1); + const [nonBlockingCount, setNonBlockingCount] = useState(1); + const [value, setValue] = useState(null); + + const { isLoading, blockMultipleClick } = useBlockPromiseMultipleClick(); + + const fetchApi = async () => { + const res = await fetch( + `https://jsonplaceholder.typicode.com/todos/${blockingCount}` + ).then((response) => response.json()); + + setValue(res); + setBlockingCount(blockingCount + 1); + }; + + const handleClick = () => { + setNonBlockingCount(nonBlockingCount + 1); + blockMultipleClick(fetchApi); // (*) Promise 반환하는 함수를 인자로 넣어주세요. + }; + + return ( +
+ +
+

BlockingCount: {blockingCount}

+

NonBlockingCount: {nonBlockingCount}

+
+ {isLoading ?

로딩중

:

{value?.title}

} +
+ ); +}; +``` + +## Example + +export const Example = () => { + const [blockingCount, setBlockingCount] = useState(1); + const [nonBlockingCount, setNonBlockingCount] = useState(1); + const [value, setValue] = useState(null); + const { isLoading, blockMultipleClick } = useBlockPromiseMultipleClick(); + const fetchApi = async () => { + const res = await fetch( + `https://jsonplaceholder.typicode.com/todos/${blockingCount}` + ).then((response) => response.json()); + setValue(res); + setBlockingCount(blockingCount + 1); + }; + const handleClick = () => { + setNonBlockingCount(nonBlockingCount + 1); + blockMultipleClick(fetchApi); + }; + return ( +
+ +
+

BlockingCount: {blockingCount}

+

NonBlockingCount: {nonBlockingCount}

+
+ {isLoading ?

로딩중

:

{value?.title}

} +
+ ); +}; + + \ No newline at end of file diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts index f16fcb893..ae78ba8c4 100644 --- a/packages/react/src/hooks/index.ts +++ b/packages/react/src/hooks/index.ts @@ -1,5 +1,6 @@ export * from './useAsyncPreservedCallback'; export * from './useAsyncProcessQueue'; +export * from './useBlockPromiseMultipleClick'; export * from './useDebounce'; export * from './useForceUpdate'; export * from './useImageStatus'; @@ -20,4 +21,3 @@ export * from './useUnMount'; export * from './useVisibilityChange'; export * from './useWindowScrollTo'; export * from './useWindowSize'; -export * from './useBlockPromiseMultipleClick'; diff --git a/packages/react/src/hooks/useBlockPromiseMultipleClick/index.ts b/packages/react/src/hooks/useBlockPromiseMultipleClick/index.ts index a2402fba0..c70c59996 100644 --- a/packages/react/src/hooks/useBlockPromiseMultipleClick/index.ts +++ b/packages/react/src/hooks/useBlockPromiseMultipleClick/index.ts @@ -1,6 +1,6 @@ import { useRef, useState } from 'react'; -export const useBlockMultipleClick = () => { +export const useBlockPromiseMultipleClick = () => { const [isLoading, setIsLoading] = useState(false); const isClicked = useRef(false); diff --git a/packages/react/src/hooks/useBlockPromiseMultipleClick/useBlockPromiseMultipleClick.spec.tsx b/packages/react/src/hooks/useBlockPromiseMultipleClick/useBlockPromiseMultipleClick.spec.tsx index 39f11ed22..46001e03c 100644 --- a/packages/react/src/hooks/useBlockPromiseMultipleClick/useBlockPromiseMultipleClick.spec.tsx +++ b/packages/react/src/hooks/useBlockPromiseMultipleClick/useBlockPromiseMultipleClick.spec.tsx @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { screen, renderHook } from '@testing-library/react'; import { renderSetup } from '../../utils/test/renderSetup'; -import { useBlockMultipleClick } from '.'; +import { useBlockPromiseMultipleClick } from '.'; const delay = (time: number) => { return new Promise((resolve) => { @@ -17,10 +17,10 @@ afterEach(() => { vi.useRealTimers(); }); -describe('useBlockMultipleClick', () => { - it('should block double click', async () => { +describe('useBlockPromiseMultipleClick', () => { + it('should block multiple button clicks until the promise in the callback function is resolved', async () => { const mockFn = vi.fn(async () => await delay(1000)); - const { result } = renderHook(useBlockMultipleClick); + const { result } = renderHook(useBlockPromiseMultipleClick); const { blockMultipleClick } = result.current; expect(result.current.isLoading).toBe(false); @@ -29,7 +29,7 @@ describe('useBlockMultipleClick', () => { const { user } = renderSetup( , - { delay: null }, + { delay: null } ); const button = screen.getByRole('button');