=> {
+ return typeof value === "object" && value !== null && !Array.isArray(value);
+};
diff --git a/packages/lib/src/hocs/deepMemo.ts b/packages/lib/src/hocs/deepMemo.ts
index 13f7f18e..7d9e00f8 100644
--- a/packages/lib/src/hocs/deepMemo.ts
+++ b/packages/lib/src/hocs/deepMemo.ts
@@ -1,5 +1,7 @@
import type { FunctionComponent } from "react";
+import { deepEquals } from "../equals";
+import { memo } from "./memo";
export function deepMemo(Component: FunctionComponent
) {
- return Component;
+ return memo(Component, deepEquals);
}
diff --git a/packages/lib/src/hocs/memo.ts b/packages/lib/src/hocs/memo.ts
index 532c3a5c..07e99746 100644
--- a/packages/lib/src/hocs/memo.ts
+++ b/packages/lib/src/hocs/memo.ts
@@ -2,5 +2,17 @@ import { type FunctionComponent } from "react";
import { shallowEquals } from "../equals";
export function memo
(Component: FunctionComponent
, equals = shallowEquals) {
- return Component;
+ let prevProps: P | null = null;
+ let memoizedElement: ReturnType | null = null;
+
+ const MemoizedComponent = (props: P) => {
+ if (!prevProps || !equals(prevProps, props)) {
+ prevProps = props;
+ memoizedElement = Component(props);
+ }
+
+ return memoizedElement;
+ };
+
+ return MemoizedComponent;
}
diff --git a/packages/lib/src/hooks/useAutoCallback.ts b/packages/lib/src/hooks/useAutoCallback.ts
index 23100162..9805ce90 100644
--- a/packages/lib/src/hooks/useAutoCallback.ts
+++ b/packages/lib/src/hooks/useAutoCallback.ts
@@ -3,5 +3,13 @@ import { useCallback } from "./useCallback";
import { useRef } from "./useRef";
export const useAutoCallback = (fn: T): T => {
- return fn;
+ const fnRef = useRef(fn);
+
+ fnRef.current = fn;
+
+ const stableFn = useCallback((...args: Parameters): ReturnType => {
+ return fnRef.current(...args);
+ }, []);
+
+ return stableFn as T;
};
diff --git a/packages/lib/src/hooks/useCallback.ts b/packages/lib/src/hooks/useCallback.ts
index 712bc898..44344669 100644
--- a/packages/lib/src/hooks/useCallback.ts
+++ b/packages/lib/src/hooks/useCallback.ts
@@ -1,7 +1,7 @@
-/* eslint-disable @typescript-eslint/no-unused-vars,@typescript-eslint/no-unsafe-function-type */
+/* eslint-disable @typescript-eslint/no-unsafe-function-type */
import type { DependencyList } from "react";
+import { useMemo } from "./useMemo";
export function useCallback(factory: T, _deps: DependencyList) {
- // ์ง์ ์์ฑํ useMemo๋ฅผ ํตํด์ ๋ง๋ค์ด๋ณด์ธ์.
- return factory as T;
+ return useMemo(() => factory, _deps);
}
diff --git a/packages/lib/src/hooks/useMemo.ts b/packages/lib/src/hooks/useMemo.ts
index e80692e2..686db97e 100644
--- a/packages/lib/src/hooks/useMemo.ts
+++ b/packages/lib/src/hooks/useMemo.ts
@@ -1,8 +1,23 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
import type { DependencyList } from "react";
+import { useRef } from "./useRef";
import { shallowEquals } from "../equals";
export function useMemo(factory: () => T, _deps: DependencyList, _equals = shallowEquals): T {
- // ์ง์ ์์ฑํ useRef๋ฅผ ํตํด์ ๋ง๋ค์ด๋ณด์ธ์.
- return factory();
+ const ref = useRef<{
+ deps: DependencyList;
+ value: T | undefined;
+ initialized: boolean;
+ }>({
+ deps: [],
+ value: undefined,
+ initialized: false,
+ });
+
+ if (!ref.current.initialized || !_equals(ref.current.deps, _deps)) {
+ ref.current.value = factory();
+ ref.current.deps = _deps;
+ ref.current.initialized = true;
+ }
+
+ return ref.current.value as T;
}
diff --git a/packages/lib/src/hooks/useRef.ts b/packages/lib/src/hooks/useRef.ts
index 285d4ae7..919854f9 100644
--- a/packages/lib/src/hooks/useRef.ts
+++ b/packages/lib/src/hooks/useRef.ts
@@ -1,4 +1,9 @@
-export function useRef(initialValue: T): { current: T } {
- // useState๋ฅผ ์ด์ฉํด์ ๋ง๋ค์ด๋ณด์ธ์.
- return { current: initialValue };
+import { useState } from "react";
+
+type Ref = { current: T };
+
+export function useRef(initialValue: T): Ref {
+ const [ref] = useState[>(() => ({ current: initialValue }));
+
+ return ref;
}
diff --git a/packages/lib/src/hooks/useRouter.ts b/packages/lib/src/hooks/useRouter.ts
index a18ae01f..fb592863 100644
--- a/packages/lib/src/hooks/useRouter.ts
+++ b/packages/lib/src/hooks/useRouter.ts
@@ -6,7 +6,7 @@ import { useShallowSelector } from "./useShallowSelector";
const defaultSelector = (state: T) => state as unknown as S;
export const useRouter = , S>(router: T, selector = defaultSelector) => {
- // useSyncExternalStore๋ฅผ ์ฌ์ฉํ์ฌ router์ ์ํ๋ฅผ ๊ตฌ๋
ํ๊ณ ๊ฐ์ ธ์ค๋ ํ
์ ๊ตฌํํฉ๋๋ค.
const shallowSelector = useShallowSelector(selector);
- return shallowSelector(router);
+
+ return useSyncExternalStore(router.subscribe, () => shallowSelector(router));
};
diff --git a/packages/lib/src/hooks/useShallowSelector.ts b/packages/lib/src/hooks/useShallowSelector.ts
index b75ed791..e48342ff 100644
--- a/packages/lib/src/hooks/useShallowSelector.ts
+++ b/packages/lib/src/hooks/useShallowSelector.ts
@@ -4,6 +4,17 @@ import { shallowEquals } from "../equals";
type Selector = (state: T) => S;
export const useShallowSelector = (selector: Selector) => {
- // ์ด์ ์ํ๋ฅผ ์ ์ฅํ๊ณ , shallowEquals๋ฅผ ์ฌ์ฉํ์ฌ ์ํ๊ฐ ๋ณ๊ฒฝ๋์๋์ง ํ์ธํ๋ ํ
์ ๊ตฌํํฉ๋๋ค.
- return (state: T): S => selector(state);
+ const prevSelectedRef = useRef](null);
+ const hasInitialized = useRef(false);
+
+ return (state: T): S => {
+ const selected = selector(state);
+
+ if (!hasInitialized.current || !shallowEquals(prevSelectedRef.current, selected)) {
+ hasInitialized.current = true;
+ prevSelectedRef.current = selected;
+ }
+
+ return prevSelectedRef.current as S;
+ };
};
diff --git a/packages/lib/src/hooks/useShallowState.ts b/packages/lib/src/hooks/useShallowState.ts
index 7b589748..c8d471a9 100644
--- a/packages/lib/src/hooks/useShallowState.ts
+++ b/packages/lib/src/hooks/useShallowState.ts
@@ -1,7 +1,19 @@
import { useState } from "react";
import { shallowEquals } from "../equals";
+import { useCallback } from "./useCallback";
-export const useShallowState = (initialValue: Parameters>[0]) => {
- // useState๋ฅผ ์ฌ์ฉํ์ฌ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , shallowEquals๋ฅผ ์ฌ์ฉํ์ฌ ์ํ ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ ํ
์ ๊ตฌํํฉ๋๋ค.
- return useState(initialValue);
+export const useShallowState = (initialValue: T | (() => T)) => {
+ const [state, setState] = useState(initialValue);
+
+ const setShallowState = useCallback((next: T) => {
+ setState((prev) => {
+ if (shallowEquals(prev, next)) {
+ return prev;
+ }
+
+ return next;
+ });
+ }, []);
+
+ return [state, setShallowState] as const;
};
diff --git a/packages/lib/src/hooks/useStorage.ts b/packages/lib/src/hooks/useStorage.ts
index fdc97a6f..1c51cd7a 100644
--- a/packages/lib/src/hooks/useStorage.ts
+++ b/packages/lib/src/hooks/useStorage.ts
@@ -4,6 +4,5 @@ import type { createStorage } from "../createStorage";
type Storage = ReturnType>;
export const useStorage = (storage: Storage) => {
- // useSyncExternalStore๋ฅผ ์ฌ์ฉํด์ storage์ ์ํ๋ฅผ ๊ตฌ๋
ํ๊ณ ๊ฐ์ ธ์ค๋ ํ
์ ๊ตฌํํด๋ณด์ธ์.
- return storage.get();
+ return useSyncExternalStore(storage.subscribe, storage.get, storage.get);
};
diff --git a/packages/lib/src/hooks/useStore.ts b/packages/lib/src/hooks/useStore.ts
index acf3ad79..ecacf423 100644
--- a/packages/lib/src/hooks/useStore.ts
+++ b/packages/lib/src/hooks/useStore.ts
@@ -7,7 +7,7 @@ type Store = ReturnType>;
const defaultSelector = (state: T) => state as unknown as S;
export const useStore = (store: Store, selector: (state: T) => S = defaultSelector) => {
- // useSyncExternalStore์ useShallowSelector๋ฅผ ์ฌ์ฉํด์ store์ ์ํ๋ฅผ ๊ตฌ๋
ํ๊ณ ๊ฐ์ ธ์ค๋ ํ
์ ๊ตฌํํด๋ณด์ธ์.
const shallowSelector = useShallowSelector(selector);
- return shallowSelector(store.getState());
+
+ return useSyncExternalStore(store.subscribe, () => shallowSelector(store.getState()));
};