ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 새로 알게 된 react-router 관련 토막 상식 (Query parameter가 바뀔 때마다 re-render 시키는 법)
    Frontend 2020. 12. 13. 21:54
    import { ExampleComponent } from './example';
    
    // ...
    <Route path='/example' component={ ExampleComponent } />

     

    회사에서 react-router를 쓰면서 주로 위와 같은 방식으로 routing을 하다가

    router v5 버전부터 아래와 같이 Route 컴포넌트 안에 child 컴포넌트를 합성하는 방식이 더 권장되는 것을 발견하고 바꾸었다.

     

    import { ExampleComponent } from './example';
    
    // ...
    <Route path='/example'>
      <ExampleComponent />
    </Route>

     

    근데 이걸로 바꾸는 순간 한 가지 이슈가 발생했는데... 😨

     

    바로 이 이슈였다. 탭을 이동할 때마다 주소 뒤에 query parameter가 바뀌고,

    이 query parameter가 바뀔 때마다 UI가 바뀌는 화면이 있었는데,

    Routing 방식을 바꾸니까 갑자기 탭을 아무리 클릭해도 화면이 안 바뀌는 것이었다(!)

     

    그래서 살펴보니, query parameter가 바뀌어도 컴포넌트가 re-render가 안되는 것이 문제였다.

    그러니까 URL path 자체는 동일하고 뒤에 query parameter만 바뀌었기 때문인 것으로 추정된다.

     

    <Route path='/example'>
      { () => <ExampleComponent /> }
    </Route>
    
    <Route
      path='/example'
      component={ ExampleComponent }
    />
    
    <Route
      path='/example'
      render={ () => <ExampleComponent /> }
    />
    

     

    즉, query paramter value가 바뀌어서 location 객체가 업데이트될 때마다 re-render를 하고 싶으면 위와 같이 사용을 해주어야만 했다.

     

     

    Component

    <Route
      path='/example'
      component={ ExampleComponent }
    />

     

     

    우리가 Route의 component 속성을 사용하면, router는 React.createElement 메소드를 통해서 새로운 리액트 엘리먼트를 생성한다. 이 말은 만약에 component에 inline function을 넣어버리면 render를 할 때마다 새 컴포넌트를 매번 만들어낸다. 따라서 이미 존재하는 컴포넌트를 업데이트하는 것이 아니라 매 번 새 컴포넌트 mount, unmount를 반복하는 것이다. 그래서 만약에 inline function을 사용하고 싶으면 render 속성을 사용하는 것이 좋다.

     

     

    Render

    <Route
      path='/example'
      render={ (routerProps) => <ExampleComponent { ...routerProps } /> }
    />

     

    render 속성을 사용하면 위에서 언급한 문제없이 inline function을 사용할 수 있게 된다. 그리고 편하게 routerProps를 새 컴포넌트에 주입해줄 수도 있다. 

     

     

     

     

     

    그렇다면, 위와 같이 component, render 속성을 이용하지 않고,

    Route 컴포넌트 안에 children으로 컴포넌트를 사용하면서도

    query parameter가 바뀌었을때 re-render가 일어나도록 하는 방법은 없는 것일까?

     

    댓츠노노! 그렇지 않다.

     

    react-router 라이브러리에서 제공하는 useLocation hook을 사용하면 가능하다.

    그러니까 query parameter를 window.location 객체에서 가져다쓰는 것이 아니라,

    useLocation() hook을 통해 가져와서 쓰면, query parameter가 바뀔 때마다 location hook을 사용하는 컴포넌트가 re-render된다.

     

    import { useLocation } from 'react-router-dom';
    import { parse } from 'query-string';
    
    function ExampleComponent () {
      const { exampleQuery } = parse(useLocation().search);
      
      //...
    }
    

     

    이 방법이 사실 더 좋은 이유는 component나 render 속성을 쓰면,

    query parameter가 바뀔때마다 지정해준 컴포넌트의 하위 컴포넌트들이 모두 re-render가 된다.

     

    그러다 그냥 <Route><ExampleComponent /></Route> 요렇게 사용하고

    필요한 곳에서만 useLocation hook을 사용하면, 이 hook을 사용하는 곳에서만 re-render가 일어난다!

    반응형

    COMMENT