React.js 에서 useTransitionuseDeferredValue 를 몰랐을 때에는 그냥 useEffect 혹은 useMemo 를 사용하여 state를 관리했었는데, 공식홈페이지를 보다가 조금 더 알게된 내용을 공부하고 포스팅한다.

Output

0. useTransition 이란?

- 일반적인 상태 업데이트를 하는데 유용한 React Hook.

useFormStatus 와 유사하게 사용할 수 있다.
( useTransition: 일반적인 상태 업데이트, useFormStatus: Form 제출 상태를 관리)

const {isPending, startTransition} = useTransition()

이런 선언으로 간단하게 시작할 수 있다.

 

일반적인 textInput 값을 사용해보자

 

1. startTransition 으로 query의 값을 변경할 때 setQuery 호출

const [query, setQuery] = useState("");
const [isPending, startTransition] = useTransition();

const handleChange = (e) => {
    startTransition(() => {
        setQuery(e.target.value);
    });
};

//컴포넌트 내용 
return (
	<input
        type="text"
        onChange={handleChange}
    />	
)

1) useTransition은 상태 업데이트를 긴급하지 않은 작업으로 처리한다.
2) isPending 상태를 통해 전환 중임을 사용자에게 표시할 수 있다.
3) React는 더 중요한 업데이트(예: 사용자 입력)를 먼저 처리한 후 이 업데이트를 실행한다.

> Lazy Loading 과 같이 지연 연산으로 처리 한다.
> 만약 onChange 가 한 번 더 호출되면, 이전 연산하던 것은 버리고 새로 연산을 시작 한다.

 

2. defferredValue 선언

const defferredValue = useDeferredValue(query);

위에서 setQuery(newValue) 를 실행할때의 조건을 설정한다고 생각하면 된다.

1번과 마찬가지로 useDeferredValue는 값의 변경을 지연시켜 UI 응답성을 유지합니다.
사용자 입력에 즉시 반응하면서도 무거운 렌더링 작업은 나중에 처리할 수 있게 해줍니다.

 

3. 실제 Filter 처리

const list = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`);
const filteredList = useMemo(() => {
    if (defferredValue === "") return list; //공백일때는 초기화

    return list.filter((item) =>
        item.toLowerCase().includes(defferredValue.toLowerCase())
    );
}, [defferredValue]);

실제로 사용할때는 query 값을 사용하는 것이 아니라, deferredValue 값을 사용해야한다. (지연처리를 위함)

추가적으로 useMemo 를 사용하였는데, 변경되지 않은 부분에 대해 불필요한 Re-Rendering 방지합니다.

 

결국, 궁극적 목표는 UI의 최적화이다. (버벅거림을 없애기)

 

전체 코드

const list = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`);
const TransitionExample: React.FC = () => {
    const [query, setQuery] = useState("");
    const [isPending, startTransition] = useTransition();

    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        startTransition(() => {
            setQuery(e.target.value);
        });
    };

    const defferredValue = useDeferredValue(query);
    
    const filteredList = useMemo(() => {
        if (defferredValue === "") return list; //공백일때는 초기화

        return list.filter((item) =>
            item.toLowerCase().includes(defferredValue.toLowerCase())
        );
    }, [defferredValue]);
    
    return (
   	<>
        <input
            className="w-full h-10 px-4 py-3 mr-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
            type="text"
            onChange={handleChange}
            placeholder="검색어를 입력하세요..."
        /> 
   
   		<ul className="divide-y divide-gray-200">
            {filteredList.map((item) => (
                <li
                    key={item}
                    className="py-2 px-3 hover:bg-gray-100 transition-colors duration-150 ease-in-out"
                >
                    {item}
                </li>
            ))}
        </ul>
	</>
  )

 

TypeScript 로 연습했기 때문에 위와 같이 React.FC 같은 Type 선언이 추가되어져 있다.

반응형

+ Recent posts