codememo

문서 사용.react에서 querySelector를 선택하십시오.대신 레퍼런스를 쓸까요? 어떻게요?

tipmemo 2023. 2. 27. 22:38
반응형

문서 사용.react에서 querySelector를 선택하십시오.대신 레퍼런스를 쓸까요? 어떻게요?

난 지금 리액트에 회전목마를 만들고 있어사용 중인 개별 슬라이드로 스크롤하려면document.querySelector다음과 같이 합니다.

useEffect(() => {
    document.querySelector(`#slide-${activeSlide}`).scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'nearest'
    });
  }, [activeSlide]);

이게 나쁜 관행인가요?결국, 내가 여기서 직접 DOM에 접속하는거잖아?리액트 방식으로는 어떤 것이 있을까요?

편집: 전체return방법

return (
    <>
      <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
      <Wrapper id="test">
        {children.map((child, i) => {
          return (
            <Slide id={`slide-${i}`} key={`slide-${i}`}>
              {child}
            </Slide>
          );
        })}
      </Wrapper>

      <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
    </>
  );

이 경우 참조를 사용할지 여부에 대해서는 답변할 수 없습니다.단, 참조는 필요 없습니다.id다른 용도로 사용하지 않는 한 가치관.

하지만 다음과 같은 방법이 있습니다.

  1. 참조를 작성하는 데 사용합니다.

    const activeSlideRef = useRef(null);
    
  2. 위에 올려놓으세요Slide현재 활성화되어 있습니다.

    <Slide ref={i === activeSlide ? activeSlideRef : null} ...>
    
  3. 고객님의 고객명useEffect, 참조를 사용합니다.current소유물

    useEffect(() => {
        if (activeSlideRef.current) {
            activeSlideRef.current.scrollIntoView({
              behavior: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
        }
    }, [activeSlide]);
    

    (내 생각에는activeSlide그 효과에 대한 합리적인 의존관계입니다.ref는 사용할 수 없습니다. ref 자체는 달라지지 않습니다...)

실제 예에서는 일부 컴포넌트를divs 편의상:

const {useEffect, useRef, useState} = React;

function Deck({children}) {
    const [activeSlide, setActiveSlide] = useState(0);
    const activeSlideRef = useRef(null);

    useEffect(() => {
        if (activeSlideRef.current) {
            activeSlideRef.current.scrollIntoView({
              behavior: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
        }
    }, [activeSlide]);

    const moveLeft = Math.max(0, activeSlide - 1);
    const moveRight = Math.min(children.length - 1, activeSlide + 1);

    return (
        <React.Fragment>
          <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
          <div id="test">
            {children.map((child, i) => {
              const active = i === activeSlide;
              return (
                <div className={`slide ${active ? "active" : ""}`} ref={active ? activeSlideRef : null} id={`slide-${i}`} key={`slide-${i}`}>
                  {child}
                </div>
              );
            })}
          </div>

          <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
        </React.Fragment>
    );
}

ReactDOM.render(
    <Deck>
      <div>slide 0 </div>
      <div>slide 1 </div>
      <div>slide 2 </div>
      <div>slide 3 </div>
      <div>slide 4 </div>
      <div>slide 5 </div>
      <div>slide 6 </div>
      <div>slide 7 </div>
      <div>slide 8 </div>
      <div>slide 9 </div>
    </Deck>,
    document.getElementById("root")
);
.slide {
  height: 4em;
  vertical-align: middle;
  text-align: center;
}
#test {
  overflow: scroll;
  max-height: 20em;
}
.active {
  font-weight: bold;
  color: blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>


코멘트에서 다음과 같이 질문했습니다.

디세블로 할 수 있는지 알고 계십니까?useEffect첫 번째 렌더링을 위해 여기에 왔나요?

비상태의 컴포넌트별 정보를 유지하려면useRef문서에서는 DOM 요소 참조뿐만 아니라 컴포넌트별 비상태 데이터도 참조할 수 있다고 지적하고 있습니다.그래서 당신은

const firstRenderRef = useRef(true);

그럼 네 안에useEffect콜백, 체크firstRenderRef.currentmndash; 이 경우true설정, 설정false그렇지 않으면 스크롤을 수행합니다.

const {useEffect, useRef, useState} = React;

function Deck({children}) {
    const [activeSlide, setActiveSlide] = useState(0);
    const activeSlideRef = useRef(null);
    // *** Use a ref with the initial value `true`
    const firstRenderRef = useRef(true);

    console.log("render");

    useEffect(() => {
        // *** After render, don't do anything, just remember we've seen the render
        if (firstRenderRef.current) {
            console.log("set false");
            firstRenderRef.current = false;
        } else if (activeSlideRef.current) {
            console.log("scroll");
            activeSlideRef.current.scrollIntoView({
              behavior: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
        }
    }, [activeSlide]);

    const moveLeft = Math.max(0, activeSlide - 1);
    const moveRight = Math.min(children.length - 1, activeSlide + 1);

    return (
        <React.Fragment>
          <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
          <div id="test">
            {children.map((child, i) => {
              const active = i === activeSlide;
              return (
                <div className={`slide ${active ? "active" : ""}`} ref={active ? activeSlideRef : null} id={`slide-${i}`} key={`slide-${i}`}>
                  {child}
                </div>
              );
            })}
          </div>

          <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
        </React.Fragment>
    );
}

ReactDOM.render(
    <Deck>
      <div>slide 0 </div>
      <div>slide 1 </div>
      <div>slide 2 </div>
      <div>slide 3 </div>
      <div>slide 4 </div>
      <div>slide 5 </div>
      <div>slide 6 </div>
      <div>slide 7 </div>
      <div>slide 8 </div>
      <div>slide 9 </div>
    </Deck>,
    document.getElementById("root")
);
.slide {
  height: 4em;
  vertical-align: middle;
  text-align: center;
}
#test {
  overflow: scroll;
  max-height: 10em;
}
.active {
  font-weight: bold;
  color: blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>


사고 실험으로 인체 공학을 좀 더 쉽게 하기 위해 후크를 썼습니다.

function useInstance(instance = {}) {
    // assertion: instance && typeof instance === "object"
    const ref = useRef(instance);
    return ref.current;
}

사용방법:

const inst = useInstance({first: true});

useEffect,한다면inst.first정말이야, 해inst.first = false;; 그렇지 않으면 스크롤을 수행합니다.

라이브:

const {useEffect, useRef, useState} = React;

function useInstance(instance = {}) {
    // assertion: instance && typeof instance === "object"
    const ref = useRef(instance);
    return ref.current;
}

function Deck({children}) {
    const [activeSlide, setActiveSlide] = useState(0);
    const activeSlideRef = useRef(null);
    const inst = useInstance({first: true});

    console.log("render");

    useEffect(() => {
        // *** After render, don't do anything, just remember we've seen the render
        if (inst.first) {
            console.log("set false");
            inst.first = false;
        } else if (activeSlideRef.current) {
            console.log("scroll");
            activeSlideRef.current.scrollIntoView({
              behavior: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
        }
    }, [activeSlide]);

    const moveLeft = Math.max(0, activeSlide - 1);
    const moveRight = Math.min(children.length - 1, activeSlide + 1);

    return (
        <React.Fragment>
          <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
          <div id="test">
            {children.map((child, i) => {
              const active = i === activeSlide;
              return (
                <div className={`slide ${active ? "active" : ""}`} ref={active ? activeSlideRef : null} id={`slide-${i}`} key={`slide-${i}`}>
                  {child}
                </div>
              );
            })}
          </div>

          <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
        </React.Fragment>
    );
}

ReactDOM.render(
    <Deck>
      <div>slide 0 </div>
      <div>slide 1 </div>
      <div>slide 2 </div>
      <div>slide 3 </div>
      <div>slide 4 </div>
      <div>slide 5 </div>
      <div>slide 6 </div>
      <div>slide 7 </div>
      <div>slide 8 </div>
      <div>slide 9 </div>
    </Deck>,
    document.getElementById("root")
);
.slide {
  height: 4em;
  vertical-align: middle;
  text-align: center;
}
#test {
  overflow: scroll;
  max-height: 10em;
}
.active {
  font-weight: bold;
  color: blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

수락된 답변에 더해 질문의 '해야 하는' 부분에 대한 답변을 시도하고 DOM 조작에 대한 참조를 사용하여 다음 사항을 작성합니다.

  • refs를 사용하면 (여러 요소가 실수로 문서와 비교하여 + 값이 같을 수 있는 id와 비교하여) 대응하는 요소를 선형 시간에 고유하게 식별하고 선택할 수 있습니다.올바른 요소를 선택하기 위해 DOM을 스캔해야 하는 query Selector)
  • ref는 react 컴포넌트의 라이프 사이클을 인식하고 있기 때문에 refact는 컴포넌트가 마운트 해제되었을 때 ref가 null로 업데이트되고 즉시 사용할 수 있는 편리성이 높아집니다.
  • 개념으로서의 refs + 구문은 플랫폼에 의존하지 않기 때문에 react native와 브라우저에서 동일한 이해를 사용할 수 있으며 쿼리 실렉터는 브라우저와 같은 것입니다.
  • SSR의 경우 DOM이 없는 경우에도 ref를 사용하여 react 요소를 타깃으로 할 수 있습니다.

물론 쿼리 셀렉터를 사용하는 것은 잘못된 것이 아니며 일반적으로 반응 세계에서 사용하는 경우 기능이 저하되지는 않지만 대부분의 경우 기본 이점이 있으므로 프레임워크에서 제공하는 것을 사용하는 것이 좋습니다.

언급URL : https://stackoverflow.com/questions/59198952/using-document-queryselector-in-react-should-i-use-refs-instead-how

반응형