Skip to content

Commit

Permalink
fix(@toss/react, @toss/use-loading): implement useIsMountedRef and …
Browse files Browse the repository at this point in the history
…modified `useLoading` to avoid state updates when the component is unmounted (toss#296)

* feat: Modify useLoading to avoid state updates when the component is unmounted

* feat: impletmenet useIMountedRef

* docs: write

---------

Co-authored-by: raon0211 <[email protected]>
  • Loading branch information
okinawaa and raon0211 authored Jun 3, 2024
1 parent 12a5872 commit d96b004
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 10 deletions.
1 change: 1 addition & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/react/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './useHotjarTracker';
export * from './useInputState';
export * from './useInterval';
export * from './useIsMounted';
export * from './useIsMountedRef';
export * from './useIsomorphicLayoutEffect';
export * from './useLiveAnnouncer';
export * from './useLocalStorageChangeDetector';
Expand All @@ -27,3 +28,4 @@ export * from './useTimeout';
export * from './useTimeoutQueue';
export * from './useToggleState';
export * from './useVisibilityEvent';

25 changes: 25 additions & 0 deletions packages/react/react/src/hooks/useIsMountedRef.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# useIsMountedRef

`useIsMountedRef` is a hook that allows you to determine whether a component is currently mounted. It is primarily used to prevent side effects, such as asynchronous operations or timers, from occurring when the component is unmounted.

## Motivation([reference](https://github.com/helderberto/use-is-mounted-ref))

- Avoid memory leaks setting states when component are unmounted;
- Control when component already mounted;
- Common error when setting state to unmounted component:

## Example

```ts
const ref = useIsMountedRef();

useEffect(() => {
if (!ref.isMounted) {
return;
}
if (clientBenefitIntelliQuery.data === undefined) {
return;
}
setBenefitIntelliContents(clientBenefitIntelliQuery.data);
}, [clientBenefitIntelliQuery.data, ref.isMounted]);
```
27 changes: 27 additions & 0 deletions packages/react/react/src/hooks/useIsMountedRef.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# useIsMountedRef

Component의 mount 여부를 알 수 있는 hook 입니다.

`useIsMountedRef` 를 이용하여 컴포넌트가 현재 마운트되어 있는지 아닌지를 알 수 있습니다. 주로 비동기 작업 또는 타이머와 같이 컴포넌트가 마운트되지 않은 상태에서 발생하는 부작용을 방지하는 데 유용합니다.

## Motivation([reference](https://github.com/helderberto/use-is-mounted-ref))

- 컴포넌트가 언마운트된 상태에서 상태(State)를 설정하여 메모리 누수를 방지합니다.
- 컴포넌트가 이미 마운트되었는지를 확인하고 제어합니다.
- 언마운트된 컴포넌트에 상태(State)를 설정할 때 흔히 발생하는 오류를 방지합니다.

## Example

```ts
const ref = useIsMountedRef();

useEffect(() => {
if (!ref.isMounted) {
return;
}
if (clientBenefitIntelliQuery.data === undefined) {
return;
}
setBenefitIntelliContents(clientBenefitIntelliQuery.data);
}, [clientBenefitIntelliQuery.data, ref.isMounted]);
```
18 changes: 18 additions & 0 deletions packages/react/react/src/hooks/useIsMountedRef.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { renderHook } from '@testing-library/react';

import { useIsMountedRef } from './useIsMountedRef';

describe('useIsMountedRef', () => {
it('should return current reference object with true value if component is mounted', () => {
const { result } = renderHook(() => useIsMountedRef());

expect(result.current).toEqual({ isMounted: true });
});

it('should return current reference object with false value if component is unmounted', () => {
const { result, unmount } = renderHook(() => useIsMountedRef());
expect(result.current).toEqual({ isMounted: true });
unmount();
expect(result.current).toEqual({ isMounted: false });
});
});
14 changes: 14 additions & 0 deletions packages/react/react/src/hooks/useIsMountedRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useEffect, useRef } from 'react';

export function useIsMountedRef() {
const ref = useRef({ isMounted: true }).current;

useEffect(() => {
ref.isMounted = true;
return () => {
ref.isMounted = false;
};
}, [ref]);

return ref;
}
3 changes: 3 additions & 0 deletions packages/react/use-loading/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"coverage": "jest --coverage",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@toss/react": "workspace:^1.5.0"
},
"devDependencies": {
"@babel/core": "^7",
"@testing-library/react": "^13.3.0",
Expand Down
30 changes: 21 additions & 9 deletions packages/react/use-loading/src/useLoading.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
/** @tossdocs-ignore */
import { useIsMountedRef } from '@toss/react';
import { useCallback, useMemo, useState } from 'react';

export function useLoading(): [boolean, <T>(promise: Promise<T>) => Promise<T>] {
const [loading, setLoading] = useState(false);
const startTransition = useCallback(async <T>(promise: Promise<T>) => {
try {
setLoading(true);
const data = await promise;
return data;
} finally {
setLoading(false);
}
}, []);
const ref = useIsMountedRef();

const startTransition = useCallback(
async <T>(promise: Promise<T>) => {
try {
setLoading(true);
const data = await promise;
return data;
} finally {
if (ref.isMounted) {
setLoading(false);
}
}
},
[ref.isMounted]
);




return useMemo(() => [loading, startTransition], [loading, startTransition]);
}
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5861,7 +5861,7 @@ __metadata:
languageName: unknown
linkType: soft

"@toss/react@workspace:^1.7.0, @toss/react@workspace:packages/react/react":
"@toss/react@workspace:^1.5.0, @toss/react@workspace:^1.7.0, @toss/react@workspace:packages/react/react":
version: 0.0.0-use.local
resolution: "@toss/react@workspace:packages/react/react"
dependencies:
Expand Down Expand Up @@ -6133,6 +6133,7 @@ __metadata:
"@babel/core": "npm:^7"
"@testing-library/react": "npm:^13.3.0"
"@toss/jest": "workspace:^"
"@toss/react": "workspace:^1.5.0"
"@toss/rollup-config": "workspace:^0.2.0"
"@types/babel__core": "npm:^7"
"@types/jest": "npm:^28.1.8"
Expand Down

0 comments on commit d96b004

Please sign in to comment.