반응: Key Prop 이해하기

면접 질문을 준비하고 있었습니다. '가상 DOM이란 무엇입니까?'라는 질문 중 하나를 검색했습니다.React 개발자의 질문 중 하나에 대한 일종의 고전적인 질문입니다. 맞습니까?

관련 게시물을 읽고 있었는데 갑자기 key 소품에 대한 다음과 같은 질문을 받았습니다.
  • 요소의 키가 달라지면 다른 변경 사항이 없더라도 새 요소로 대체됩니까?
  • 요소의 속성이나 텍스트가 변경되더라도 요소가 이전 요소와 동일합니까?
  • React는 메모리에 RealDOM과 비교하기 위한 virtualDOM이 있으며 필요한 부분을 업데이트합니다.
    이것이 React가 렌더링에 작동하는 방식에 대해 내가 아는 전부입니다. 더 많은 정보를 검색하여 Reconciliation 설명서에서 React에 대해 읽었습니다.

    Reconciliation은 DOM 업데이트를 위해 React가 작동하는 프로세스입니다.

    문서에서

    If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. Instead, React implements a heuristic O(n) algorithm based on two assumptions:

    Two elements of different types will produce different trees.
    The developer can hint at which child elements may be stable across different renders with a key prop.



    키가 같으면 뭔가 잘못될 수 있기 때문에 인덱스를 key 소품으로 사용하면 안 된다는 것을 알고 있었습니다.
    하지만 어떤 것도 테스트해 본 적이 없었기 때문에 거기에서 어떤 일이 일어날지 확신할 수 없었기 때문에 오늘 key 소품에 대해 파헤치기로 했습니다.

    키는 React가 변경된 요소를 식별하는 데 도움이 됩니다.



    import { useState } from 'react';
    
    function ExampleA() {
      const [value, setValue] = useState(false);
    
      const toggle = () => setValue(!value);
    
      return (
        <div>
          {value ? (
            <div
              style={{ color: 'red' }}
              onClick={() => {
                alert('hello');
              }}
            >
              Hello
            </div>
          ) : (
            <div>Bye</div>
          )}
          <button onClick={toggle}>Toggle</button>
        </div>
      );
    }
    
    export default ExampleA;
    

    누군가는 값에 따라 다른 div 요소를 렌더링한다고 생각할 수 있습니다. (RealDOM에서)



    동일한 div 태그입니다. 속성을 변경하는 것이 좋습니다. 요소가 아닙니다.





    요소를 변수에 저장했습니다. 토글 버튼을 누른 다음 변수를 확인했습니다.
    그들은 동일합니다.

    하지만 키가 다르다면?

    import { useState } from 'react';
    
    function ExampleA() {
      const [value, setValue] = useState(1);
    
      const toggle = () => setValue(value > 0 ? 0 : 1);
    
      return (
        <div>
          {value ? <div key={value}>Hello</div> : <div key={value}>Bye</div>}
          <button onClick={toggle}>Toggle</button>
        </div>
      );
    }
    
    export default ExampleA;
    


    코드는 다음과 같습니다.





    RealDOM에 있던 요소가 제거되고 새 요소가 생성됩니다.

    인덱스를 키로 사용하여 .map을 사용하여 배열 렌더링




    import { useEffect, useState, useMemo } from 'react';
    
    function Time({ time }: { time: string }) {
      useEffect(() => {
        console.log({ time });
      }, [time]);
    
      return <div>{time}</div>;
    }
    
    function ExampleB() {
      const [times, setTimes] = useState<string[]>([]);
    
      const addItem = () => {
        setTimes([new Date().toString(), ...times]);
      };
    
      const elements = useMemo(() => {
        return times.map((time, timeIdx) => <Time key={timeIdx} time={time} />);
      }, [times]);
    
      return (
        <div>
          <button type="button" onClick={addItem}>
            Add Item
          </button>
          <hr />
          {elements}
        </div>
      );
    }
    
    export default ExampleB;
    


    항목을 추가하면 어떻게 되는지 살펴보겠습니다.







    항목을 추가할 때마다 모든 항목이 업데이트됩니다.

    const elements = useMemo(() => {
        return times.map((time) => <Time key={time} time={time} />);
      }, [times]);
    


    키를 time로 변경했습니다. 그리고 다시 보자.





    이제 잘 작동하는데 왜 제대로 작동하지 않습니까?
    이 사진들을 보세요.

    키로 인덱스






    timekey


    key는 요소를 구별하는 데 사용됩니다. 이상이 없어 보여도 관리를 해야 합니다.

    다른 예를 보자.

    import { useState, useMemo } from 'react';
    
    function ExampleC() {
      const [times, setTimes] = useState<string[]>([]);
    
      const addItem = () => {
        setTimes([new Date().toString(), ...times]);
      };
    
      const elements = useMemo(() => {
        const handleDelete = (timeIdx: number) => () => {
          setTimes((prevTimes) => prevTimes.filter((_, idx) => idx !== timeIdx));
        };
    
        return times.map((time, timeIdx) => (
          <div key={timeIdx}>
            <div>time: {time}</div>
            <div>
              <label>memo</label>
              <input type="text" />
            </div>
            <button type="button" onClick={handleDelete(timeIdx)}>
              Delete
            </button>
            <hr />
          </div>
        ));
      }, [times]);
    
      return (
        <div>
          <button type="button" onClick={addItem}>
            Add Item
          </button>
          <hr />
          {elements}
        </div>
      );
    }
    
    export default ExampleC;
    




    항목이 3개 있는데 두 번째 항목을 삭제하겠습니다.



    두 번째input의 텍스트는 'CCCC'가 아니라 'BBBB'입니다.
    왜요?


    Reactkey 3가 제거된 것으로 인식하므로 'CCCC'가 있는 입력란은 key 3의 자식이므로 삭제되며, 예, key 2의 시간은 '00:02'에서 변경됩니다. ' -> '00:01'.


    결론



    나는 편집이나 삭제 기능이 없을 때 indexkey 소품으로 자주 사용했습니다. 잘 작동하는 것 같았기 때문입니다.
    지금은 그렇지 않았을 수도 있다는 것을 알게 되었고 key 소품을 좀 더 신중하게 다루도록 하겠습니다.
    나는 이것이 누군가에게 도움이되기를 바랍니다 :)

    행복한 코딩!

    좋은 웹페이지 즐겨찾기