쾌락코딩

react에서 scroll down 구현

|

리액트에서 스크롤 다운을 구현하기위해 열심히 구글링을 하다가, 몇가지 기록할만한 것들을 발견했다.

element.scrollIntoView

스크롤 다운 버튼을 눌렀을 때, 보여지고 싶은 element에서 scrollIntoView메서드를 호출하면 된다. 이 메서드에는 몇가지 options을 줄 수 있다. options는 그리 어렵지도않고 다양하지도 않아서 간단히 MDN 링크로만.

element 참조를 위한 ref

react에서는 해당 element를 ref를 사용해 참조하면 된다. 문제는 typescript에서 ref를 사용하여 참조하려니까 조금 까다로운 점이 있었다(그래서 사실 내가 구현한 방법도 좋은 방법은 아닌 것 같지만 좋은 방안을 찾으면 바꿔야겠다.)

만약 react 16.3+ 버전을 사용한다면 React.createRef() 간단하게 참조할 수 있다고 한다.

class TestApp extends React.Component<AppProps, AppState> {
    private stepInput: React.RefObject<HTMLInputElement>;
    constructor(props) {
        super(props);
        this.stepInput = React.createRef();
    }
    render() {
        return <input type="text" ref={this.stepInput} />;
    }
}

위의 코드와 같이 HTMLInputElement타입 뿐만 아니라 HTMLDivElement와 같은 대부분의 요소 타입이 존재한다. 만약 div태그를 참조하고 싶다면 HTMLDivElement태그를 사용하면 된다.

그러나 나는 같은 아래와 같은 상황에서 IntroTwo를 참조하여 스크롤 다운 버튼을 눌렀을 때 바로 IntroTwo부분을 보여주고 싶었다.

import IntroTwo from '../component/Main/IntroTwo';

class IntroContainer extends React.Component<IProps, IState> {
  private introTwoLocationRef: React.RefObject<IntroTwo>;

  constructor (props: any) {
    super(props);
    this.introTwoLocationRef = React.createRef();
  }

  onClickScrollDownButton = () => {
    console.log(this.introTwoLocationRef)
  };

  render () {
    return (
      <React.Fragment>
        <IntroOne onClickScrollDownButton={this.onClickScrollDownButton} />
        <IntroTwo ref={this.introTwoLocationRef}/> // 요거
      </React.Fragment>
    );
  }
}

위와 같은 코드를 작성하면 될 줄 알았는데, onClickScrollDownButton으로 찍힌 콘솔을 보면 div나 input을 참조하고 콘솔을 찍었을 때 나오는 것들이 하나도 나오지 않았다. 에러가 난건 아니지만 element가 아닌 클래스라 그런지 내가 원했던 속성들이 없었고 따라서 scrollIntoView을 사용할 수가 없었다(생각해보면 introTwoLocationRef은 엘리먼트가아니라 클래스이기 때문에 scrollTop값은 속성이 없는게 당연한 듯). 그래서 우선은 임시방편(?)으로 아래와 같은 코드를 작성해 원하는 기능을 구현하긴 했다.

import IntroTwo from '../component/Main/IntroTwo';

class IntroContainer extends React.Component<IProps, IState> {
  private introTwoLocationRef: React.RefObject<HTMLDivElement>;

  constructor (props: any) {
    super(props);
    this.introTwoLocationRef = React.createRef();
  }

  onClickScrollDownButton = () => {
    const introTwoLocation: HTMLDivElement = this.introTwoLocationRef
      .current as HTMLDivElement;
    introTwoLocation.scrollIntoView({
      behavior: 'smooth',
      block: 'start'
    });
  };

  render () {
    return (
      <React.Fragment>
        <IntroOne onClickScrollDownButton={this.onClickScrollDownButton} />
        <div style= ref={this.introTwoLocationRef} />
        <IntroTwo />
      </React.Fragment>
    );
  }
}

중간에 임의의 div 요소를 넣고 height을 0px로 주면, div와 IntroTwo의 scrollTop은 같은 위치를 가르키기 때문에 div요소에서 scrollIntoView을 호출하면 IntroTwo가 보여진다.

정리

이렇게 구현하는게 뭔가 좋지는 않은…찜찜한 느낌이 든다. 자식 컴포넌트가 랜더링하는 많은 element중 하나를 참조할 수 있는 방법을 찾아보고 적용해봐야겠다. 분명히 있긴한것 같은데 typescript라 그런지 자료가 많지는 않은 것 같다.

Comments