TIL29. 리액트 컴포넌트 및 hooks

imloopy·2022년 6월 1일
0

Today I Learned

목록 보기
32/56

오늘 배운 것들

  • component
    • Flux component
    • Tab component
  • custom-hooks
    • useHover
    • useKey & useKeypress
    • useScroll & useRafState
    • useClickAway

Flux component

  • flux component는 칼럼을 만드는데 사용하는 컴포넌트이다. Flux에 대하여 좀 더 찾아보고자 구글에 검색을 해 보았으나, Flux 디자인 패턴만 나오고 Flux 컴포넌트 내용은 별로 없었다. bootstrap에 columns라는 비슷한 컴포넌트가 있었다. 아마 column으로 부르는 것 같다.
  • flux 컴포넌트는 row와 col로 이루어져있으며, 12등분이 되어있다. col은 span이란 속성을 갖고, span 의 배수만큼의 너비를 row 내부에서 차지한다. 또한, FluxProvider에 gutter 값을 입력받을 수 있게 하여 gutter를 설정할 수 있도록 했다.
  • Flex 컴포넌트 내부에서 단을 설정할 필요가 있을 때 좋을 것 같다. 다만, margin이나 padding 속성을 사용하지 않고 flex의 gap 속성을 사용한다면 어떨 까라는 생각도 든다. 다만, 이전에 단순히 CSS로 두 단의 flex 칼럼을 만들었었는데, gap 속성을 이용하면 각 단의 width를 50%로 맞춘다면 두 단이 생기지 않고 너비 차이에 의해 하나의 단만 만들어졌던 것으로 기억한다. 이 부분은 한번 더 확인해 보아야겠다.
  • 만약 반응형 웹에 대응하기 위하여 컴포넌트를 설정할 때, flex: 2 등의 속성을 사용하는 것과는 어떤 차이가 있는지, 이것도 한번 더 실습해봐야겠다.

Tab Component

  • 오늘 배웠던 컴포넌트중에 가장 이해가 가지 않았던 부분이다. 일단 컴포넌트 만드는 것 자체가 아직 익숙하지 않고, 근데 children을 가공하는 방식 또한 익숙하지 않았던 부분이라 익숙하지 않은 부분이 너무 많았다.

Tab 컴포넌트의 역할

  • 탭을 클릭하면 현재 클릭한 탭의 아이템을 보여줌
  • 이때 모든 아이템들은 preloaded 되어 있어야 함

TabItem

  • TabItem은 현재 활성화가 되어있는지, 되어 있지 않은지 확인하는 컴포넌트이다.
  • active(현재 활성화가 되어있는지 확인하기 위한 boolean prop), title, index(key값으로 사용)을 전달받는다.

TabWrapper

  • TabWrapper는 현재 활성화 되어있는 인덱스(몇 번을 클릭했는지)의 값을 저장한다. (useState) 외부 prop으로 기본 값을 전달받을 수 있으면, 기본값을 설정한다.
  • 만약 prop으로부터 전달받은 기본 값이 없다면, children에 설정한 index를 이용하여, 해당 인덱스를 prop으로 설정한다. prop을 인덱스가 아닌 해당 컴포넌트의 고유 인덱스 값을 이용하여 설정하기 위함이다.
  • Tabitem에 새로운 프롭들을 넣어주기 위하여 cloneElement하여 반환 받은 값을 자식에 삽입한다. 그리고 해당 아이템을 재사용하기 위하여 useMemo로 메모이제이션한다.

개인적으로는 왜 이런 방식으로 사용해야 하는지 이해가 잘 안간다.(아직 배움이 부족해서 그렇다.) React.cloneElement를 사용하는 부분도, 하위 아이템의 개수를 모를 경우 유용하다고 하지만 데이터를 배열 형식으로 프롭으로 전달 받은 뒤, map을 이용하여 element를 만들어주면 되는 것이 아닌지, 그리고 거기서 해당 아이템에 필요한 프롭들 또한 반복문으로 손 쉽게 전달 가능한 것이 아닌지, 두 방법간 어떤 차이가 있는지가 매우 궁금하다. (두 컴포넌트간 의존성을 낮추기 위하여?)

useHover hook

  • useHover hook은 사이드 이펙트 처리하는 부분을 분리하고 재사용하기 위하여 만든 hook이다. useRef를 이용하여 어떤 컴포넌트를 참조하도록 하였고, 해당 컴포넌트 위에 mouseenter가 되어 있다면 hover = true, 그렇지 않으면 hover = false를 반환하도록 하였다.

useKey와 useKeypress hook

  • useKey는 키를 눌렀을 때 콜백 함수를 실행하기 위해 만든 hook이다.
const useKey = (
  targetKey: string,
  handler: (...data: unknown[]) => void,
  event: 'keyup' | 'keypress' | 'keydown' = 'keydown',
) => {...}
  • 어떤 키를 눌렀을 때 콜백을 실행해야 하는지 (targetKey, handler), 어떤 이벤트에서 실행을 할 것인지 (event)를 받아서 이벤트 리스너를 등록하고, 컴포넌트가 제거되면 이벤트를 remove한다.
  • useKeypress 해당 키가 눌렸는지, 눌리지 않았는지 검증하기 위한 hook이다. 기본적인 구현 방법은 uesKey와 비슷하지만, 어떤 함수를 실행하기 위한 목적으로 만든 hook이 아니므로 단순히 isKeyPressed라는 boolean 값을 반환하도록 만들었다.

useScroll & useRafState hook

useScroll hook은 스크롤 이벤트가 발생할 때 현재 위치를 반환하는 hook이다. 모든 컴포넌트에서 동작할 수 있도록 ref 객체를 만들었으며, 해당 DOM의 상대적인 위치를 반환하도록 ref.current.scrollTop, ref.current.scrollLeft를 기록하도록 하였다.

그러나 scrollevent는 매우 빠르게 일어나기 때문에, reflow 또한 엄청나게 많이 발생하기 때문에 scroll 이벤트를 최적화 해주는 작업이 필요하다.

  • ref.current.addEventListener('scroll', callback, { passive: true } 옵션을 사용하여 event.preventDefault()를 호출하지 못하게 한다. → e.preventDefault를 호출하면 메인스레드에 영향을 미치므로, 이를 방지하기 위함이다.
  • requestanimationframe으로 프레임 제한

useRafstate hook

먼저 프레임을 최적화 하기 위해서는 requestAnimationFrame과, cancelAnimationFrame에 대하여 이해하고 있어야 한다.

  • requestAnimationFrame
    • 다음 페인트 과정이 진행되기 전에, requestAnimationFrame에 등록한 콜백 함수를 실행한다.
  • cancelAnimationFrame(requestId)
    • 인자로받은 requestId에 스케쥴된 애니메이션 요청을 취소한다.

따라서 RafState를 이용하여 최적화 하는 방식은 다음과 같다.

  1. useRafState는 외부로부터 defaultValue를 입력받는다.
  2. useRaftState내부에 현재 requestId를 저장할 useRef로 담긴 mutable 변수를 만든다. useRef로 만드는 이유는 다시 렌더링이 되더라도 값은 남아있지만, 해당 변수가 변경됨에 따른 DOM업데이트가 필요 없기 때문이다.

setRafState 콜백 함수 내부

  1. ref.current에 저장된 requestID 값을 이용하여, cancelAnimationFrame을 호출하여 이전에 등록한 스케쥴을 제거한다.
  2. ref.current에 현재 스케쥴의 requestID를 저장한다. requestAnimationFrame(() ⇒ setValue(T))를 ref.current에 저장하면 된다.

useRafState로부터 반환받은 콜백 함수를 이용하면, 프레임이 실행될 때 값의 변경이 발생되고, 나머지 경우에는 값의 변경이 발생되지 않는다. 그렇기 때문에 scroll event를 통한 함수 호출 횟수와 디스플레이의 주사율을 일치시킬 수 있으므로, 불필요한 함수 호출을 방지할 수 있다.

느낀 점

  • 리액트의 custom hook을 사용하는 방식에 조금 더 익숙해진 것 같다.
  • 다만, useScroll 이벤트를 requestAnimationFrame으로 처리하는 방법은 처음 배웠는데, 렌더링과 함수 호출 횟수를 일치시킨다는 개념이 참신하면서도 효율적이라는 생각이 들었다. customhook으로 빼 놓았기 때문에 다시 사용하기에도 굉장히 좋으므로, 적절한 사용처를 한 번 고민해 보아야겠다.
  • cloneElement를 이용하는 방식이 맘에 안든다. 내가 맘에 안든다고 해서 사용하지 않을 것은 아니다만, 아직 왜 굳이 data를 입력받아 map으로 컴포넌트를 만드는 방식을 대신하여 사용해야 하는지에 대해 뭐가 더 나은지 알지 못했다. 자료를 한번 찾아봐야겠다.
  • 할 것이 태산인데, 리액트가 vue보다 어려워서 그런지 아직 진도가 빨리 나가지 않는다… 언젠간 쉬워질 날이 오겠지..?

출처

Gutters - Bootstrap
React.childrentoarray - React
requestAnimationFrame - mdn
cancelAnimationFrame - mdn

0개의 댓글