동시성을 위한 18개의 새로운 후크 반응!

동시성은 React 18 에서 중요한 변경 사항입니다.

다음 후크를 살펴보겠습니다.
  • useId useId는 수화 불일치를 방지하면서 클라이언트와 서버 모두에서 고유한 ID를 생성하기 위한 새로운 후크입니다. 고유 ID가 필요한 접근성 API와 통합되는 구성 요소 라이브러리에 주로 유용합니다. 이는 React 17 이하에 이미 존재하는 문제를 해결하지만 새로운 스트리밍 서버 렌더러가 HTML을 순서 없이 전달하는 방식 때문에 React 18에서 훨씬 더 중요합니다. See docs here.
  • useTransition useTransition 및 startTransition을 사용하면 일부 상태 업데이트를 긴급하지 않은 것으로 표시할 수 있습니다. 다른 상태 업데이트는 기본적으로 긴급한 것으로 간주됩니다. React는 긴급하지 않은 상태 업데이트(예: 검색 결과 목록 렌더링)를 중단하기 위해 긴급한 상태 업데이트(예: 텍스트 입력 업데이트)를 허용합니다. See docs here
  • useDeferredValue useDeferredValue를 사용하면 트리의 긴급하지 않은 부분을 다시 렌더링하는 것을 연기할 수 있습니다. 디 바운싱과 비슷하지만 그에 비해 몇 가지 장점이 있습니다. 고정된 시간 지연이 없으므로 React는 첫 번째 렌더링이 화면에 반영된 직후 지연 렌더링을 시도합니다. 지연된 렌더링은 중단 가능하며 사용자 입력을 차단하지 않습니다. See docs here.

  • 이 후크를 코드로 설명하겠습니다. 철저하지 않습니다.
    빠른 보기를 제공하고 싶습니다.

    더 자세한 내용을 알고 싶으면 Google에 검색하면 온라인에서 많은 자료를 찾을 수 있습니다.

    시작하기 전에 ReactDOM.render를 사용하는 경우 createRoot로 바꿉니다.

    * createRoot: 렌더링하거나 마운트 해제할 루트를 만드는 새로운 방법입니다. ReactDOM.render 대신 사용하십시오. React 18의 새로운 기능은 그것 없이는 작동하지 않습니다. See docs here.

    저는 그냥 이렇게 설정했습니다.

    import React from 'react';
    import { createRoot } from 'react-dom/client';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    const container = document.getElementById('root') || document.body;
    const root = createRoot(container);
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();
    



    사용 ID


    uuid를 사용하여 이전에 노드 또는 다른 것을 식별하기 위한 고유 ID를 생성한 적이 있습니까?
    이제 'useId'를 사용할 수 있습니다.

    import React, {
      useState,
      useCallback,
      useMemo,
      useRef,
      useEffect,
      useId,
    } from 'react';
    
    interface TimerItem {
      id: string;
      createdAt: Date;
      tm: NodeJS.Timeout;
    }
    
    let num = 1;
    let count = () => {
      return num++ % 10000;
    };
    
    function Timer() {
      const [timers, setTimers] = useState<TimerItem[]>([]);
      const [workIn, setWorkIn] = useState(false);
      const id = useId(); // generate uniqueId
      const delUniqueId = useRef<string | null>(null);
    
      const toggle = useCallback(() => setWorkIn((prev) => !prev), []);
    
      const addTimer = useCallback(() => {
        // create new timer
        const itemId = `${id}${count()}`;
        const newItem = {
          id: itemId,
          createdAt: new Date(),
          tm: setTimeout(() => {
            const tmInv = setInterval(() => {
              if (!delUniqueId.current) {
                // insert this uniqueId into delUniqueId to remove and execute worker using toggle
                delUniqueId.current = itemId;
                toggle();
                // if delUniqueId is changed successfully, clear this timer
                clearInterval(tmInv);
              }
            }, 50);
          }, 2000),
        };
    
        setTimers((prevTimers) => [...prevTimers, newItem]);
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, []);
    
      useEffect(() => {
        if (!delUniqueId.current) return;
    
        // remove a timer by delUniqueId
        setTimers(timers.filter((t) => t.id !== delUniqueId.current));
        delUniqueId.current = null;
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [workIn]);
    
      const children = useMemo<React.ReactNode>(() => {
        return (
          <>
            {timers.map((timer) => (
              <div key={timer.id}>
                <span>
                  Timer / {timer.id} / {timer.createdAt.getMinutes()}::
                  {timer.createdAt.getSeconds()}
                </span>
              </div>
            ))}
          </>
        );
      }, [timers]);
    
      return (
        <div>
          <button onClick={addTimer}>Add Timer</button>
          <hr />
          {children}
        </div>
      );
    }
    
    function App() {
      return (
        <>
          <Timer />
          <Timer />
          <Timer />
        </>
      );
    }
    




    3개Timer를 렌더링합니다. 각 타이머 구성 요소에는 uniqueid가 있습니다. 데이터가 있는 ID로 식별할 수 있습니다.

    :r1:, :r3:, :r5: 봤어?

    예, 좋은 예인지 잘 모르겠습니다.

    어쨌든 useId를 사용하여 uniqueid를 생성할 수 있습니다.

    하지만, 참고하세요

    useId is not for generating keys in a list. Keys should be generated from your data.* See docs



    useTransition, startTransition



    일부 상태 업데이트는 긴급하지 않고 다른 상태 업데이트는 기본적으로 긴급한 것으로 간주됩니까?

    긴급하지 않은 상태 업데이트에는 startTransition를 사용하십시오.

    import React, {
      useEffect,
      useState,
    } from 'react';
    
    const nodes: React.ReactNode[] = [];
    
    for (let i = 1; i <= 5000; i++) {
      nodes.push(<div>{Math.random() * i}</div>);
    }
    
    function App() {
      const [text, setText] = useState('');
      const [random, setRandom] = useState<React.ReactNode[]>([]);
    
      useEffect(() => {
        if (!text) return;
          setRandom(nodes);
      }, [text]);
    
      return (
        <>
          <input
            type="text"
            onChange={(e) => setText(e.target.value)}
            value={text}
          />
          <>{random}</>
        </>
      );
    }
    




    다음은 예입니다.
    보시다시피 타이핑할 때 타이핑이 거의 멈췄습니다.

    다른 구성 요소 렌더링(아래 난수 목록)이 급하지 않다고 생각되면 'startTransition'을 이렇게 사용할 수 있습니다.

    import React, { useEffect, useState, startTransition } from 'react';
    
    const nodes: React.ReactNode[] = [];
    
    for (let i = 1; i <= 5000; i++) {
      nodes.push(<div>{Math.random() * i}</div>);
    }
    
    function App() {
      const [text, setText] = useState('');
      const [random, setRandom] = useState<React.ReactNode[]>([]);
    
      useEffect(() => {
        if (!text) return;
        startTransition(() => {
          setRandom(nodes);
        });
      }, [text]);
    
      return (
        <>
          <input
            type="text"
            onChange={(e) => setText(e.target.value)}
            value={text}
          />
          <>{random}</>
        </>
      );
    }
    




    약간의 멈춤 현상이 있기는 하지만(어쨌든 다른 구성 요소는 렌더링해야 함) 이전보다 확실히 나아졌습니다.

    로딩이 필요한 경우 useTransition를 사용할 수 있습니다.

    import React, { useEffect, useState, useTransition } from 'react';
    
    const nodes: React.ReactNode[] = [];
    
    for (let i = 1; i <= 5000; i++) {
      nodes.push(<div>{Math.random() * i}</div>);
    }
    
    function App() {
      const [text, setText] = useState('');
      const [random, setRandom] = useState<React.ReactNode[]>([]);
      const [isPending, startTransition] = useTransition();
    
      useEffect(() => {
        if (!text) return;
        startTransition(() => {
          setRandom(nodes);
        });
      }, [text]);
    
      return (
        <>
          <input
            type="text"
            onChange={(e) => setText(e.target.value)}
            value={text}
          />
          {isPending ? 'loading...' : <>{random}</>}
        </>
      );
    }
    




    useDeferredValue



    무언가의 변화가 다른 렌더링에 영향을 줍니까?
    하지만 어떤 것의 변화를 먼저 렌더링해야 하고 다른 것이 그 뒤에 따라와도 괜찮습니까?
    useDeferredValue를 사용하십시오.

    import React, { useState, useMemo } from 'react';
    
    function App() {
      const [text, setText] = useState('');
    
      const random = useMemo<React.ReactNode>(() => {
        const children: React.ReactNode[] = [];
    
        for (let i = 1; i <= 3000; i++) {
          children.push(<div>{Math.random() * i}</div>);
        }
    
        return children;
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [text]);
    
      return (
        <>
          <input
            type="text"
            onChange={(e) => setText(e.target.value)}
            value={text}
          />
          <>{random}</>
        </>
      );
    }
    




    다음은 예입니다.
    텍스트 변경에 따라 3000개의 임의 노드를 렌더링합니다.
    딜레이가 많죠?

    사용하자useDeferredValue
    import React, { useDeferredValue, useState, useMemo } from 'react';
    
    function App() {
      const [text, setText] = useState('');
      const deferredText = useDeferredValue(text);
    
      const random = useMemo<React.ReactNode>(() => {
        const children: React.ReactNode[] = [];
    
        for (let i = 1; i <= 1000; i++) {
          children.push(<div>{Math.random() * i}</div>);
        }
    
        return children;
      }, [deferredText]);
    
      return (
        <>
          <input
            type="text"
            onChange={(e) => setText(e.target.value)}
            value={text}
          />
          <>{random}</>
        </>
      );
    }
    






    deferredText를 useMemo의 의존성으로 사용했습니다.
    디바운싱과 비슷합니다.


    결론



    18개의 새로운 후크에 반응하세요! 다른 새로운 기능이 있습니다.
    프로젝트에 적용하기 전에 Google에서 검색하는 것이 좋습니다.
    사용자를 위한 동시 렌더링 전략을 세우십시오.
    React가 우리에게 또 하나의 힘을 주었습니다 :)

    즐거운 코딩하세요!

    좋은 웹페이지 즐겨찾기