별 건 아니지만 Jotai & useMemo 사용법에 대해

dante Yoon·2022년 10월 26일
5

별 건 아니지만

목록 보기
8/11
post-thumbnail

별 거지만

썸네일 만들어주신 보리님 감사합니다.

shoutout to my friendly neighbor qhflrnfl4324

별 건 아니지만 시리즈로는 오랜만에 포스팅하네요.

시나리오

여러 리엑트 컴포넌트에서 특정 돔을 조회해야 하는 경우가 있었습니다.

예를 들어 이런 상황입니다.

좌측 1번, 2번 컴포넌트의 특정 버튼을 누를 시 우측 컴포넌트 내부에 있는 돔의 위치를 변경시켜야 합니다.

이럴 때는 어떻게 해야 할까요?

두 가지 시나리오가 있을 수 있습니다.

  • 우측 컴포넌트의 상태 값을 변경: 이 경우 우측 컴포넌트 내부에서 상태 값에 따른 돔 이동 로직을 작성해주어야 함
  • 1,2번 컴포넌트에서 해당 돔을 직접 조작: 이 경우 상태 값을 조작하는 것이 아닌 해당 돔을 직접 조작합니다.

리엑트에서 돔을 직접 조작하는 경우는 많지 않으니 상태 값을 조작하는게 맞을까요?

꼭 그렇지는 않습니다.

1번 시나리오로 가봅시다.

동그라미는 상태입니다. 1,2,3번 컴포넌트가 우측 컴포넌트의 상태 값을 조작하면 이 컴포넌트에서 결국 저 돔을 조작해야 합니다.

그런데 말입니다.

저 상태 값은 어떻게 좌측 컴포넌트에서 업데이트 시킬 수 있을까요?

두 가지 방법이 있습니다.

  • recoil, jotai 같은 atomic한 상태 값을 선언하던가
  • 상위 컴포넌트에서 글로벌한 스토어를 만들던가

결국 1,2번과 우측 컴포넌트를 포함하는 상위 컴포넌트에서 상태 값을 만들어서 참조시켜야 합니다.

복잡하네요.
그래서 저는 상태값을 업데이트 하지 않고 1,2번 컴포넌트에서 돔을 직접 조작할 수 있게 만들겁니다.

근데 이렇게 하려면 우측 컴포넌트에서 돔이 마운트 될 때까지 기다린 다음에 1,2번 컴포넌트에서 해당 값을 참조할 수 있어야 합니다.

useEffect

근데 우리 useEffect는 지양하기로 했잖아요

두둥!

저는 Jotai를 사용하고 있는데요, jotai에는 atom이라는 primitive type이 있습니다. useEffect 대신에 atom이 어떻게 돔을 참조하게 할 수 있을 지 고민이 되었는데요.

그래서 물어봤습니다.

누구한테?
Jotai 만든 사람한테.

dai-shi 와의 대화

https://github.com/pmndrs/jotai/discussions/1488#discussioncomment-3942031

정확한 번역은 아닙니다.

안녕하세요, 저는 useEffect를 별로 좋아하지 않는데요, 돔에 대한 ref를 얻을 수 있는 시점에 맞춰서 atom을 업데이트하려면 어떻게 해야 할까요

저도 별로 안좋아해요. 근데 이 경우 useEffect나 useCallback을 써야 할 것으로 보이네요.

useEffect 안쓰고 싶은데 써야 할 것 같네요.

그러게요 써야 할 것 같네요.

정 쓰기 싫으면 atom의 onMount 사용해보실 수 있겠네요.
근데 useEffect 쓰는게 가독성에는 더 나을 수 있겠네요.

제가 useEffect를 안쓰는 이유는 가독성이 안좋아지기 때문입니다.

이렇게 써볼 수 있겠네요.

atom onMount, useMemo

dai-shi san이 제안한대로 atom의 onMount를 사용해 useEffect를 대체해봅시다.
atom onMount는 atom이 처음으로 읽어질 때 실행됩니다.

mountingAtom 은 아래 코드에 useMemo를 사용해 a 라는 atom을 참조합니다.

그리고 useAtom을 사용해 바로 아래 줄에서 실행됩니다.

실행될 때 ref 에 대한 정보가 이미 div ref props를 통해서 존재하기 때문에

외부에 있는 htmlRefAtom에 div dom 정보를 세팅할 수 있게 됩니다.

const htmlRefAtom = atom(null)

const Component = () => {
  const ref = useRef()
  const mountingAtom = useMemo(() => {
    const a = atom(
      (get) => get(htmlRefAtom),
      (get, set, arg) => set(htmlRefAtom, arg)
    )
    a.onMount = (set) => {
      set(ref.current)
    }
    return a
  }, [])
  useAtom(mountingAtom)
  return (
    <div ref={ref}>Hello</div>
  )
}

아주 괜찮은 방법이네요.

jotai와 side effect를 결합 할때 한 가지 방법이 될 것 같습니다.

좋은 참조가 되었길 바랍니다.

별 거 아니지만

구독자 100명이 되었습니다.

profile
성장을 향한 작은 몸부림의 흔적들

2개의 댓글

comment-user-thumbnail
2022년 10월 28일

기다리고 있었습니다. 제대로 모시겠습니다.

답글 달기
comment-user-thumbnail
2022년 11월 17일

👍

답글 달기