import { useState } from "react";
import { shallowEquals } from "../equals";
import { useRef } from "./useRef";
export const useShallowState = <T>(initialValue: Parameters<typeof useState<T>>[0]) => {
// 1. useState 를 사용하여 실제 상태 관리.
const [currentState, originSetState] = useState(initialValue);
// 2. 반환할 originSetState 함수 정의.
const customSetState = (newVal: unknown) => {
// 새롭게 전달 받은 값을 계산. 함수인 경우를 고려하여 새로운 값을 도출
const actualNewVal = typeof newVal === "function" ? newVal(currentState) : newVal;
// 얕은 비교 사용하여 새로운 값과 현재 값을 비교 (상태 변경을 감지)
if (!shallowEquals(actualNewVal, currentState)) {
// 비교 값이 다르다면, origin 을 호출하여 상태 값 업데이트
originSetState(actualNewVal);
}
};
// 3. 화면이 여러번 그려져도 참조를 고정
const callbackRef = useRef(customSetState);
return [currentState, callbackRef.current];
};
import { useState } from "react";
import { shallowEquals } from "../equals";
import { useCallback } from "./useCallback";
export const useShallowState = <T>(initialValue: T | (() => T)): [T, (newState: T | ((prevState: T) => T)) => void] => {
// 1. useState를 사용하여 실제 상태 값과 원본 setState 함수를 관리.
const [state, originalSetState] = useState(initialValue);
// 2. useShallowState가 외부에 반환할 setState 함수를 useCallback으로 감싸서 참조를 고정.
// 이 함수는 originalSetState의 함수형 업데이트를 사용하여 항상 최신 prevState를 받습니다.
// 따라서 이 함수 자체는 외부 스코프의 'state' 변수에 의존할 필요가 없습니다.
const customSetState = useCallback((newState: T | ((prevState: T) => T)) => {
originalSetState((prevState) => {
// <-- 변경점 1: originalSetState를 함수형으로 호출
// newState가 함수 형태일 경우, 최신 prevState를 사용하여 실제 새 값을 계산합니다.
const actualNewValue =
typeof newState === "function"
? (newState as (prevState: T) => T)(prevState) // <-- 변경점 2: prevState 사용
: newState;
// 최신 prevState와 계산된 actualNewValue를 shallowEquals로 비교합니다.
if (!shallowEquals(actualNewValue, prevState)) {
// <-- 변경점 3: prevState 사용
// 두 값이 다르면, actualNewValue로 상태를 업데이트합니다.
return actualNewValue;
}
// 두 값이 같으면, 이전 상태를 그대로 반환하여 상태 업데이트를 건너뛰고 리렌더링을 방지합니다.
return prevState;
});
}, []); // <-- 변경점 4: 의존성 배열을 비움
// 현재 상태 값과 안정적인 setState 함수를 반환합니다.
return [state, customSetState]; // <-- 변경점 5: customSetStateRef.current 대신 customSetState 반환
};

변경점 및 설명
useRef 임포트 제거 및 useCallback 임포트 추가:
customSetState 함수를 useCallback으로 감쌈:
originalSetState의 함수형 업데이트 사용 (변경점 1, 2, 3):
이전: originSetState(actualNewVal);
이후: originalSetState(prevState => { ... return actualNewValue; });
설명: customSetState 함수 내부에서 originalSetState를 호출할 때,
직접 actualNewValue를 전달하는 대신 함수(콜백 함수)를 전달하도록 변경했습니다.
prevState)을 기반으로 작동하게 되어 오래된 클로저 문제를 완벽하게 해결합니다.useCallback의 의존성 배열 비움 (변경점 4):
반환 값 변경 (변경점 5):
💡
useShallowState는 setState 함수의 참조 동일성을 유지하면서도,
항상 최신 상태 값을 기반으로 shallowEquals 비교를 수행하여 불필요한 리렌더링을 방지하는
목표를 달성하게 됩니다.