React 18 - 성능 개선

26419 단어 javascriptreact
최근 React는 몇 가지 훌륭한 기능을 갖춘 버전 18을 출시했습니다.
이 게시물에서는 성능 관련 기능에 대해 자세히 살펴보겠습니다.

useTransition



상태 업데이트의 우선 순위를 지정할 수 있는 동시성 개념의 일부입니다.
긴급한 상태 업데이트는 덜 긴급한(차단) 업데이트보다 우선 순위를 지정할 수 있습니다.

이를 사용하는 방법과 이 새로운 후크가 앱 성능을 향상시키는 방법은 찾을 수 있는 예제에서 배울 수 있습니다here.

이것은 우리의 예입니다. 간단합니다. 모달을 여는 버튼이 있고 모달 내에서 500개의 댓글 목록을 렌더링합니다.
500개의 댓글은 많지만 대부분의 기기에서 잘 작동합니다.

import { useState } from "react";
import Comments from "../components/Comments";
import Modal from "../components/Modal";
import data from "../data/index.json";

export default function Home() {
  const [isOpen, setIsOpen] = useState(false);
  const [comments, setComments] = useState([]);

  return (
    <div className="p-4">
      <button
        className="px-4 py-2 border-none rounded-sm bg-blue-800 text-white"
        onClick={() => {
          setIsOpen(true);
          setComments(data);
        }}
      >
        Toggle modal
      </button>
      <Modal
        isOpen={isOpen}
        onClose={() => {
          setIsOpen(false);
          setComments([]);
        }}
      >
        <Comments comments={comments} />
      </Modal>
    </div>
  );
}


그러나 Comment 구성 요소의 렌더링 속도를 늦추면 😈 상황이 더욱 흥미로워집니다.
이를 달성하기 위해 백만 번 반복하는 루프for를 추가했습니다.

const Comment = ({ name, email, body, className, onClick }: CommentProps) => {
  const soooSloww = [];

  for (let i = 0; i < 1000000; i++) {
    soooSloww.push(i);
  }

  return (
    <article className={className} onClick={onClick}>
      <h3 className="font-semibold">{name}</h3>
      <h4 className="text-gray-500 italic">{email}</h4>
      <p>{body}</p>
    </article>
  );
};


이제 버튼을 클릭하여 모달을 열면 몇 초 동안 아무 일도 일어나지 않습니다.
이는 브라우저가 느린 500개Comment 구성 요소를 렌더링하느라 바쁘기 때문입니다.
얼마 후 모달과 주석이 렌더링됩니다.

사용자 관점에서 이것은 매우 나쁜 UX입니다.
그것을 개선하는 방법?
렌더링의 우선 순위를 지정할 수 있으며 이 예에서는 먼저 모달을 렌더링하고 그 주석을 렌더링하는 것이 더 중요합니다.
useTransition 후크는 전환이 아직 완료되지 않았다는 부울 플래그인 pending와 덜 중요한 상태 업데이트를 실행하는 함수인 startTransition 두 변수를 반환합니다.

const [pending, startTransition] = useTransition();


이제 우리의 예는 다음과 같습니다

export default function Home() {
  const [isOpen, setIsOpen] = useState(false);
  const [comments, setComments] = useState([]);
  const [pending, startTransition] = useTransition();

  return (
    <div className="p-4">
      <button
        className="px-4 py-2 border-none rounded-sm bg-blue-800 text-white"
        onClick={() => {
          setIsOpen(true);
          startTransition(() => {
            setComments(data);
          });
        }}
      >
        Toggle modal
      </button>
      <Modal
        isOpen={isOpen}
        onClose={() => {
          setIsOpen(false);
          setComments([]);
        }}
      >
        {pending ? "Loading..." : <Comments comments={comments} />}
      </Modal>
    </div>
  );
}


버튼을 클릭하면 우선 순위가 더 높은 작업인 모달을 표시하도록 상태를 업데이트하고 React에 상태 업데이트가 우선 순위가 더 낮다는 것을 알려주는 startTransition 함수 내에서 주석 상태를 업데이트한다는 것을 알 수 있습니다.

또한 pending 플래그를 사용하여 느린 댓글이 렌더링되는 동안 사용자에게 '로드 중...' 텍스트를 표시했습니다.
이제 버튼을 클릭하면 다음과 같은 모달이 즉시 나타납니다.



훨씬 더 나은 사용자 경험! 😀

useDeferredValue



또한 이 후크는 React에 특정 상태 업데이트의 우선순위가 낮다고 알려줍니다.
그것은 useTransition 와 유사하며 솔직히 말해서 useDeferredValue 보다 useTransition 를 선호해야 하는 사용 사례가 무엇인지 잘 모르겠습니다. 아이디어가 있으면 의견에 알려주십시오. 👇

이전 예제는 이제 다음과 같으며 pending 플래그가 없다는 점을 제외하면 유사하게 동작합니다.

export default function UseDeferredValues() {
  const [isOpen, setIsOpen] = useState(false);
  const [comments, setComments] = useState([]);
  const commentsToRender = useDeferredValue(comments);

  return (
    <div className="p-4">
      <button
        className="px-4 py-2 border-none rounded-sm bg-blue-800 text-white"
        onClick={() => {
          setIsOpen(true);
          setComments(data);
        }}
      >
        Toggle modal
      </button>
      <Modal
        isOpen={isOpen}
        onClose={() => {
          setIsOpen(false);
          setComments([]);
        }}
      >
        <Comments comments={commentsToRender} />
      </Modal>
    </div>
  );
}



자동 배치



React로 작업할 때 가능한 한 재렌더링을 적게 하는 것을 목표로 해야 합니다.
이제 React 18은 자동 일괄 처리를 통해 이를 달성할 수 있도록 도와줍니다.

이전 버전의 React는 onClick 또는 onChange와 같은 React 이벤트 핸들러 내에서만 여러 상태 업데이트를 일괄 처리하여 여러 번의 재렌더링을 방지하고 성능을 향상시켰습니다.

이제 React는 React 이벤트 핸들러, 약속, setTimeout, 기본 이벤트 핸들러 등에서 상태 업데이트를 일괄 처리합니다.

const AutomaticBatching = () => {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState(0);

  console.log("render");

  const onClick = useCallback(() => {
    setCountOne(countOne + 1);
    setCountTwo(countTwo + 1);
  }, [countOne, countTwo]);

  useEffect(() => {
    document.getElementById("native-event").addEventListener("click", onClick);

    return () =>
      document
        .getElementById("native-event")
        .removeEventListener("click", onClick);
  }, [onClick]);

  const onClickAsync = () => {
    fetch("https://jsonplaceholder.typicode.com/todos/1").then(() => {
      setCountOne(countOne + 1);
      setCountTwo(countTwo + 1);
    });
  };

  const onClickTimeout = () =>
    setTimeout(() => {
      setCountOne(countOne + 1);
      setCountTwo(countTwo + 1);
    }, 200);

  return (
    <div className="p-4">
      <ul className="mb-8">
        <li>Count one: {countOne}</li>
        <li>Count two: {countTwo}</li>
      </ul>
      <Button onClick={onClick}>Batching in click event</Button>
      <Button id="native-event" className="ml-4">
        Batching in native click event
      </Button>
      <Button className="ml-4" onClick={onClickAsync}>
        Batching in fetch
      </Button>
      <Button className="ml-4" onClick={onClickTimeout}>
        Batching in timeout
      </Button>
    </div>
  );
};


이 예에서는 모든 이벤트 핸들러에서 상태 변경이 두 번 있지만 다시 렌더링은 한 번만 있음을 알 수 있습니다. 각 이벤트에 대해 하나의 console.log가 있음을 알 수 있습니다.

향상된 서스펜스


SuspenseReact.lazy와 함께 작동하여 로드될 때까지 구성 요소 렌더링을 일시 중단하고 해당 시간 동안 대체를 렌더링합니다.

const LazyComponent = lazy(() => import("../components/LazyComponent"));

<Suspense fallback={<div>Loading...</div>}>
  <LazyComponent />
</Suspense>


이는 즉시 필요하지 않은 앱의 일부(예: 모달)를 초기 번들에 포함하지 않는 방식으로 성능을 향상시키는 좋은 방법입니다.

그러나 Suspense는 새로운 기능이 아니라 이전 버전의 React에 존재했으며 새로운 기능은 이제 이전에는 없었던 서버 측 렌더링과 함께 작동한다는 것입니다.

여기까지입니다. 새 버전의 React가 마음에 드시기 바랍니다. 😀
위의 모든 예에서 찾을 수 있습니다here.

좋은 웹페이지 즐겨찾기