Home React v18 톺아보기 1편 - useTransition
Post
Cancel

React v18 톺아보기 1편 - useTransition

피그미님 벨로그

React v18 useTransition를 톺아보자 🤗

⭐️ useTransition


제가 좀 바빠서요..

useTransition은 한마디로 동작에 우선순위를 부여하는 기능이다.
이전의 리액트에는 당장 처리해야하는 동작이 있어도 실행중인 동작이 끝날 때까지 대기해야 했다.
하지만 React v18의 useTransition 기능을 사용하면 동작에 우선순위를 부여하며, Concurrent 하게 동작하게 한다. 사실 자세히 보자면 useTrasition을 부여한 동작을 우선순위를 미루고, 다른 동작을 먼저 처리하는 것이다.
아래의 예시는 검색창의 인풋 값을 검색 결과에 반영하는 예시인데 이 기능을 소개하는데 아주 적절하다.
인풋 값이 변경 될때마다 검색 결과가 수십만건으로 가정해보았다.
아래의 짤로 준비해놨지만 직접 경험해보고 싶을 것 같아서 배포해놨다.
useTransition 테스트장 으로 가서 직접 경험해보길 🤗

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { useState, useTransition } from "react";
const dummy = ["이게", "바로", "지옥이다"];
const UseTransition = () => {
  const [isChecked, setIsChecked] = useState(false);
  const [input, setInput] = useState("");
  const [searchResult, setSearchResult] = useState<string[]>([...Array(5000)]);
  const [isPending, startTransition] = useTransition();
  const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInput(e.target.value);
    if (isChecked) {
      // useTransition 적용
      startTransition(() => {
        const newArray = searchResult.map(
          (_, index) => dummy[index % dummy.length] + Math.random()
        );
        setSearchResult(newArray);
      });
    } else {
      // useTransition 미적용
      const newArray = searchResult.map(
        (_, index) => dummy[index % dummy.length] + Math.random()
      );
      setSearchResult(newArray);
    }
  };

  const handleCheckbox = (e: React.ChangeEvent<HTMLInputElement>) => {
    setIsChecked(e.target.checked);
  };

  return (
    <FunctionContainer>
      <h1>useTransition 테스트장</h1>
      <CheckboxContainer>
        <input onChange={handleCheckbox} type="checkbox" id="exampleCheckbox" />
        <CheckboxLabel htmlFor="exampleCheckbox">
          useTransition 적용하기
        </CheckboxLabel>
      </CheckboxContainer>
      <SearchInput onChange={handleInput} type="text" placeholder="Search..." />
      <ResultList>
        {isPending && <div>Loading...</div>}
        {searchResult.map((item) => (
          <ResultItem>{item}</ResultItem>
        ))}
      </ResultList>
    </FunctionContainer>
  );
};

적용 전


인풋 값이 버벅이면서 입력된다.

입력 값이 바뀔때 마다 아래에 수십만건의 검색결과가 렌더링된다.
리액트는 렌더링 동작이 끝날 때까지 대기해야하고, 마친 후에야 인풋 값이 바뀌기 때문에 인풋 값이 버벅이면서 입력된다.
짤로만 봐도 심각한 수준의 버벅임이 느껴지는데 실제 서비스라면 끔찍하다.
사용자는 검색창에 원하는 값을 입력할텐데 검색창의 값이 즉각적으로 바뀌는게 아니라 끊겨서 입력되는 것을 느껴야 한다.
이러한 문제를 해결하기 위해 해결사 useTransition을 사용해 Concurrent 한 동작을 시켜보자 💫

적용 후


인풋 값이 깔끔하게 입력된다.

위의 코드에서 적용된 부분을 한번 보자.

1
2
3
4
5
6
7
// useTransition 적용
startTransition(() => {
  const newArray = searchResult.map(
    (_, index) => dummy[index % dummy.length] + Math.random()
  );
  setSearchResult(newArray);
});

startTransition을 사용하면 내부의 콜백함수가 실행되는 시점을 의도적으로 미룰 수 있고, 우선 순위에 따라 작업을 번갈아가면서 처리함으로써 Concurrent한 동작이 가능하다.
짤을 보게되면 입력값이 깔끔하게 입력되는 것을 볼 수 있다.
또 자세히보면 입력 값이 변경 될때마다 Loading...이라는 문구가 렌더링 되는데 이는 useTransitionisPending이라는 상태를 관리하고 있기 때문이다.
isPendingstartTransition이 실행되면 true가 되고, startTransition이 실행된 콜백함수가 실행되면 false가 된다.

1
2
3
4
  const [isPending, startTransition] = useTransition();
  return(
    {isPending && <div>Loading...</div>}
  )

즉, startTransition을 사용해서 미뤄진 동작이 실행되기 전에 다른 동작이 실행되면 Loading...이라는 문구를 렌더링시키는 것이다.
여기까지 useTransition을 사용해서 Concurrent한 동작을 체험해보았고, 글이 도움이 되었으면 좋겠다.

🙏🏻 Reference


This post is licensed under CC BY 4.0 by the author.

하이라이터즈 JS -> TS로 마이그레이션 01

-