이재승님의 실전 리액트 정리하기(2)

이종호·2021년 6월 21일
3

React

목록 보기
2/5

순수 JS vs React로 화면을 그릴 때

순수 JSReact
프로그래밍
방식
- 명령형
- 화면을 어떻게 그릴 것인지

- UI가 어떻게 보일지 한 눈에 보이지 않는다.
- 선언형
- 화면에 무엇을 그릴 것인지

- UI가 어떻게 변경될지 한 눈에 보인다.(컴포넌트가 복잡하지 않을때..)
추상화- 추상화 단계가 낮다.
- 다른 환경에서 코드를 재활용하기 어렵다.
- 추상화 단계가 높다.
- 다른 환경에서 코드를 재활용하기 쉽다
(ex. react native)

Component가 반환할 수 있는 것

  • 리액트 요소
  • 컴포넌트
  • 문자열, 숫자, 배열(key값 필요 있어야 vDom을 효율적으로 가능)
  • Fragment(=== React.Fragment === <>)
  • boolean(을 이용해 조건부 렌더링에 사용)
  • React Portal

React Portal

  • html에서 root말고 다른 엘리먼트에 렌더링하고 싶을 때 주로 사용한다.
  • 모달을 위해 사용되곤 한다.
import ReactDOM from 'react-dom';

...
export defualt function App() {
  ...
  {
    ReactDOM.createPortal(
      <div>나는 포탈!</div>,
      document.getElementById('portalId')
    )
  }
}

리액트 요소와 가상돔

정리

리액트가 JSX에서 실제돔 변경까지의 과정
1. JSX는 바벨에 의해 리액트 요소로 변경되고,
2. 리액트 요소는 하위 리액트 요소의 type이 모두 문자열(DOM형식)이 될 때까지 호출하며 리액트 요소 트리를 생성한다.
3. 모든 값이 변경되면 실제 돔으로 변경될 수 있는 상태이고 이 상태가 바로 가상돔 상태다.
4. 가상 돔은 이전 가상돔과 메모리에서 비교과정을 거치며, 변경된 사항만 실제 돔에 적용한다.
5. key값의 변경, Component가 조건부 렌더링등을 통한 재생성은 요소 부분이 아닌 해당 DOM 전체를 다시 그린다.

React.memo를 이용하면, 가상돔을 그리는 과정에서 다시 컴포넌트를 랜더하여 그리지 않고, 이전의 리액트 요소를 가져와 쓰기 때문에, 효율적.

Babel은 JSX을 React.createElement로 바꿔주는 역할도 수행


리액트 요소는 UI의 표현 수단.

리액트 요소 =

React.createElement(
  'a',
  { href: 'http://google.com' },
  'click here'
)

JSX도 Babel에 의해 결국 React.createElement를 내부적으로 호출하는 결과

React를 썻을 때 렌더링이 효율적인 이유: 가상돔을 통해 실제 DOM 변경 최소화

리액트는 memory에 가상돔을 올려두고
렌더링 할 때마다 이전 가상돔과 최신 가상돔을 비교해 변경된 사항만 실제돔에 반영한다.

실제 DOM 전체가 바뀌는 2가지 경우

  1. key값 변경 시
  2. 조건부 렌더링 등을 통해 컴포넌트 다시 그리기

React에서 key값이 변경되면 아예 새로운 컴포넌트로 인식한다.

React.Element

{
  type: 'a' or Component함수, // Dom은 문자열, 컴포넌트는 함수 이름이 들어간다.
  key: key에 지정된 값,
  ref: '',
  props: {
    children,
    다른 속성 키:}
  //...
}
  • 리액트 요소는 불변 객체이다.

React에서 데이터 변경에 의한 화면 업데이트느느
2가지 단계를 가지는데,
랜더 단계와 커밋 단계이다.

랜더 단계

  • 실제 돔에 반영될 변경사항들을 파악
  • 리액트 요소 트리로 만들어진 가상돔을 통해 비교
  • 1 렌더링 1 가상돔 생성

커밋 단계

  • 파악된 변경사항들로 실제 돔을 변경하는 과정

React.memo

를 통해 해당 컴포넌트가 보유한 props가 변경되지 않는 이상, 이전에 memort에 기록한 결과를 재사용

리액트 랜더 단계는 보통 2가지 이유로 호출됨
1. ReactDOM.render 호출
2. 컴포넌트 내부의 상태값 변경

🙄아직까지 이 사실이 얼마나 유용한 정보인지는 모르겠다.

리액트 훅 기초 익히기

정리

  • 상태 값 변경 함수는 "비동기"이며 "배치"적으로 작동한다
  • 하나의 함수에서 같은 상태값 변경함수 2번 적용할 수 있다.(원래는 없다.)
  • unstable_batchedUpdates()를 통해 외부 함수도 배치적으로 작동시킬 수 있다.
  • 화면이 그려지기전에 부수함수가 호출되어야 한다면 useLayout를 쓸 수 있다.

훅.

  • 컴포넌트에 기능을 추가할 때 이용
  • React 16.8에서 추가됨

ex) 상태 값 추가, 자식 요솟 접근

Class를 사용할 때 보다 어떤 이점이 있는가?

이재승님의 kakao if conference 강의 영상자료


대표적인 2가지 훅

  • useState
  • useEffect(ex. 서버 API호출, 이벤트 핸들러 등록 or 해제)

상태 값 변경 함수는 "비동기"이며 "배치"적으로 작동한다.

만약 상태 값 변경 함수가 동기방식을 취하게 된다면
하나의 상태값 변경 함수가 호출될 때마다. 화면을 다시 그려야 한다.
그렇지 않다면 실제 데이터와 화면의 상태가 불일치 하는 상황이 그려진다.

하나의 함수에서 같은 상태값 변경함수 2번 적용하기

function addCountDouble() {
  setCount(count + 1);
  setCount(count + 1);
}

// count는 1만 증가한다.

이는 상태 값 변경 함수는 "비동기"이며 "배치"적으로 작동하기 때문인데,
어떤 이유로 하나의 함수에서 같은 상태값 변경함수를 두번 호출하고 싶을 때가 있다.
그러면 할 수 있는 방법이 상태값 변경 함수에서 함수로 변경하는 것이다.

function addCountDouble() {
  setCount(count => count + 1);
  setCount(count => count + 1);
}

// count는 2가 증가된다!!

그리고 이런 비동기적이고 배치적인 방식의 적용 범위는 해당 컴포넌트 내부에서 선언된 함수일 때만 그렇다.
그렇다면 리액트외부에서 선언된 함수는 "배치"방식으로 처리하고 싶다면 이 함수를 이용하면 된다.

ReactDOM.unstable_batchedUpdates(() => {
  setCount();
  setCount();
})

앞에 unstable이라는 무시무시한 preffix가 붙여져 있지만, 많은 사람들이 사용한다고 하니 일단은 필요하면 쓰면 될 것 같다.

참고로 미래의 react concurrent모드는 외부의 함수도 배치방식으로 바뀐다고 한다.

useEffect, useLayoutEffect

useEffect는 렌더링 후 화면이 그려지고 나서 비동기적으로 호출된다.

만약 화면이 그려지기 전에 부수효과를 처리해야 한다면 useLayoutEffect가 있다.

useEffect, useLayoutEffect 비교 참고 사이트

useEffectuseLayoutEffect
render렌더가 화면에 그려진 후 비동기적으로 실행렌더링 후 화면이 업데이트 되기 전에 동기적으로 실행
사용 경우☘️ 일부 상태를 즉시 발생할 필요가 없을 경우
☘️ 페이지에 시각적으로 영향을 주지 않는 무언가를 동기화 할 경우
☘️ 이벤트 핸들러를 설정하는 경우
☘️ 모달 상자가 나타나거나 사라질 때 일부 상태를 재설정하는 경우
☘️ 상태가 업데이트 될 때 요소가 깜박이는 경우
☘️ DOM을 변경하려는 경우

재승님의 말로는 useLayout은 가급적 사용하지 말라고 설명했다.
이유는 화면이 그려지기 전에 동기적으로 일을 처리하기 때문에, 사용자에게 화면을 보여주기까지 시간이 더 오래 소요되기 때문이다.

useEffect의 의존성배열

의존성배열을 잘 관리하지 못하면 여러 에러가 발생할수 있는데,
넣는 값으로는
useEffect에서 사용되는

  • 컴포넌트의 상태 값
  • 속성 값
  • 내부에서 정의된 지역변수, 지역함수
    라 한다.

그런데 지역 함수의 경우 실제로 의존성 배열에 넣게 된면, 너무 많이 호출된다는 경고문구가 나오는데,
이를 해결하기 위해 useCallback훅을 이용해야 한다고 했다.
아직 써본적 없어서 봐야 한다.
대략적인 기능은 해당 지역함수가 언제 호출될지 결정하는 함수 메모이제이션 기능이라고 한다.
그렇게 어려울 것 같지는 않은데.. 많이 써봐야 작 적용되지 않을까 싶다.

나중에 의존성 배열을 합리적으로 넣는 방법에 대해 소개해 주신다고 한다.

커스텀 훅 만들기

정리

  • 프로그래밍에서 함수로 로직을 기능별로 나누는 것 처럼 커스텀 훅이 그런 역할을 맡는다.
  • use로 시작하는 것이 관례이다.
  • 훅에 props가 변경되면 자동으로 훅이 다시 호출되고,
  • 훅의 내부 상태가 변경되면, 해당 훅을 사용하는 컴포넌트도 다시 렌더링한다.

ex)

  1. 서버 사이드 렌더링시 마운트 여부
    useMounted() => isMounted
  1. 로그인된 사용자만 접근 허용
    useBlockIfNotLogin() =>
  1. 저장되지 않은 상태에서 나가려 할 때,
    useBlockUnsavedChange()
  1. 로컬스토리지 활용
    uselocalStorage(key, init) => [value, setValue]

훅 사용시 주의점.

  • 훅을 호출하는 순서는 동일해야 한다.

리액트에서 훅들은 호출된 순서로 기억한다.
따라서 호출된 순서가 바뀌어 버리면 예상치 못한 버그가 발생할 수 있다.

profile
코딩은 해봐야 아는 것

1개의 댓글