Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
804 views
in Technique[技术] by (71.8m points)

javascript - Using document.querySelector in React? Should I use refs instead? How?

I am building a carousel right now, in React. To scroll to the individual slides I am using document.querySelector like so :

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

Is this bad practice? After all, I am accessing the DOM directly here? What would be the React way of doing this?

edit: full return method

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>
    </>
  );
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I can't answer the "should you" part of whether to use refs for this instead other than if you do, you don't need those id values unless you use them for something else.

But here's how you would:

  1. Use useRef(null) to create the ref.

    const activeSlideRef = useRef(null);
    
  2. Put it on the Slide that's currently active

    <Slide ref={i === activeSlide ? activeSlideRef : null} ...>
    
  3. In your useEffect, use the ref's current property

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

    (I think activeSlide is a reasonable dependency for that effect. You can't use the ref, the ref itself doesn't vary...)

Live example, I've turned some of your components into divs for convenience:

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...