React 스터디 - React 학습하기 (2)

오다혜·2024년 11월 10일
0

State 관리하기

State 구조 선택하기

  1. 연관된 state 그룹화하기. 
    두 개 이상의 state 변수를 항상 동시에 업데이트한다면, 단일 state 변수로 병합하는 것을 고려하기
  2. State의 모순 피하기. 
    여러 state 조각이 서로 모순되고 “불일치”할 수 있는 방식으로 state를 구성하는 것은 실수가 발생할 여지를 만든다.
  3. 불필요한 state 피하기.
    렌더링 중에 컴포넌트의 props나 기존 state 변수에서 일부 정보를 계산할 수 있다면, 컴포넌트의 state에 해당 정보를 넣지 않아야 함.
  4. State의 중복 피하기. 
    여러 상태 변수 간 또는 중첩된 객체 내에서 동일한 데이터가 중복될 경우 동기화를 유지하기가 어려움. 가능하다면 중복을 줄이기
  5. 깊게 중첩된 state 피하기. 
    깊게 계층화된 state는 업데이트하기 쉽지 않으므로 가능하면 state를 평탄한 방식으로 구성하기

=> state 로 사용할 것, 그렇지 않은 것을 잘 구분할 필요가 있다. state 여야 하는지 꼭 반문해볼 것!

State 의 중복 피하기

  • Props를 상태로 “미러링”하는 것은 특정 prop에 대한 모든 업데이트를 무시하기를 원할 때에만 의미가 있다.
  • prop의 이름을 initial 또는 default로 시작하여 새로운 값이 무시됨을 명확히 해야 한다.
function Message({ initialColor }) {
  // The `color` state variable holds the *first* value of `initialColor`.
  // Further changes to the `initialColor` prop are ignored.
  const [color, setColor] = useState(initialColor);

컴포넌트 간 State 공유하기

  • 제어 컴포넌트 / 비제어 컴포넌트 중 어떤 것을 사용할 지 고민하자.
    • 제어 컴포넌트를 사용하기를 추천
  • 여러 state 를 두지 말고 한 곳에서 관리할 것
  • props 를 내려서 관리할 것
    => DB 정규화와 유사

State를 보존하고 초기화하기

  • react 는 UI 트리에만 관심이 있음. 이전 렌더링과 현재 렌더링에서 동일한 위치로 컴포넌트 렌더링하는 경우 state 가 초기화되지 않을 수 있음
  • 3항연산자로 컴포넌트 렌더링 하는 경우에는 동일한 위치라고 판단, Boolean 으로 나누면 다른 위치로 인식한다.

탈출구

Ref로 DOM 조작하기

  • ref callback 으로 배열을 받을 수 있음. Map 을 유지,clean up 함수에서 delete 시켜줄 수도 있음
  • 다른 컴포넌트의 ref 를 접근하는 것은 자제해야 한다. 고수준의 컴포넌트에서는 구조 의존성을 피하고자 일반적으로 노출 x
    => 왜? 힘든 사례가 있는 건가?
  • flushSync >> ref 를 동기적으로 업데이트 시켜주는 함수 state 업데이트를 감싸면 됨.
  • 첫 렌더링 시에 dom이 생성되지 않아서 null임
  • useImperativeHandle 로 expose 를 제한할 수 있음. Vue 의 useExpose 랑 동일한 기능.
  • selectedRef 를 조건적으로 반환할 수 있음
  • 렌더링 중에 ref 변경 절대 안 됨.

Effect 가 필요하지 않은 경우

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);

  // 🔴 피하세요: Effect에서 prop 변경 시 state 조정하기
  useEffect(() => {
    setSelection(null);
  }, [items]);
  // ...
}
function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);

  // 더 좋습니다: 렌더링 중 state 조정
  const [prevItems, setPrevItems] = useState(items);
  if (items !== prevItems) {
    setPrevItems(items);
    setSelection(null);
  }
  // ...
}

=> state 변경을 렌더링 중에 최대한 계산하기를 추천한다.

item 이 바뀔 때마다 selection 을 초기화 해주고 싶을 때, useEffect 에서 처리하지 말고 차라리 prevItem 을 저장하는 state 을 둬라

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selection, setSelection] = useState(null);

  // 더 좋습니다: 렌더링 중 state 조정
  const [prevItems, setPrevItems] = useState(items);
  if (items !== prevItems) {
    setPrevItems(items);
    setSelection(null);
  }
  // ...
}

React는 return 문으로 종료된 후 즉시 List를 다시 렌더링 합니다.

⇒ return 문이 없는데 어디서 종료를 시킨다는 걸까?

렌더링 도중 컴포넌트를 업데이트하면 React는 반환된 JSX를 버리고 즉시 렌더링을 다시 시도합니다.

⇒ 반환된 JSX 를 버림

매우 느린 연속적 재시도를 피하기 위해 React는 렌더링 중에 동일한 컴포넌트의 state만 업데이트할 수 있도록 합니다. 렌더링 도중 다른 컴포넌트의 state를 업데이트하면 에러가 발생합니다.

⇒ store 등을 업데이트 시키면 안 되는듯..?

컴포넌트를 순수하게 유지하기 위해 DOM 변경이나 타임아웃 설정과 같은 다른 사이드 이펙트들은 이벤트 핸들러나 Effect에 남겨둬야 합니다.

스터디 2차 후기

느낀점

탈출구에서 다루는 useEffect 관련 내용들을 학습하니 'useEffect 를 제대로 이해하고 사용한 것이 아니었구나' 라는 생각이 들었습니다.

궁금한 점

  1. JSX 를 children 으로 넘기는 것, children 이 아닌 props 로 넘기는 것이 차이가 있을까?
  2. useContext 만 해도 상태관리하기에 괜찮은 것 같은데, 전역 관리 라이브러리를 사용하는 이유는 무엇이 있을까?
  3. tanstack query 와 같은 상태관리 라이브러리도 내부적으로 context 를 사용하는가?
  4. vue의 provide-inject 랑 어떤 차이가 있을까
  5. useEffectEvent 없이는 리렌더링 없이 어떻게 구현할 수 있을까?
profile
프론트엔드에 백엔드 한 스푼 🥄

0개의 댓글