⚛React hook

5 minute read

이번엔 hooks에 대해서 정리해보려 한다.
쓰다보니 너무 편해서 돌아가기가 힘들다. Typescript에서 좀 더 클래스를 이용한 OOP를 해보고 싶은데, 어떻게해야 좋을지 고민을 해봐야 할 것 같다. 문법보다도 객체지향 원리를 hooks와 함께 잘 녹일 수 있다면 좋은 스타일이 될 수 있을 것 같다.
(functional로도 못할 것 없다고 본다)

1. 도입 배경

  • React는 컴포넌트에 재사용 가능한 행동을 붙이는 방법을 제공하지 않습니다. 만약 이전부터 React를 사용해왔다면, 이것을 해결하기 위해 render props 그리고 고차 컴포넌트와 같은 패턴에 익숙할 것입니다. 그러나 이런 패턴을 사용할 때 컴포넌트를 재구성해야 하며 코드를 추적하기 어렵게 만듭니다.

  • Hook를 사용하면 컴포넌트로부터 상태 관련 로직을 추상화할 수 있습니다. 이것은 독립적인 테스트와 재사용이 가능합니다. Hook는 계층 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와줍니다.
    => 예를 들어 setCount를 전달해서 자식 컴포넌트가 쉽게 부모의 상태를 변경할 수 있다

  • Hook를 사용하면 컴포넌트로부터 상태 관련 로직을 추상화할 수 있습니다. 이것은 독립적인 테스트와 재사용이 가능합니다. Hook는 계층 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와줍니다.

2. useState

  • 왜 createState가 아닌, useState로 이름을 지었을까요? 컴포넌트가 렌더링할 때 오직 한 번만 생성되기 때문에 “Create”라는 이름은 꽤 정확하지 않을 수 있습니다. 컴포넌트가 다음 렌더링을 하는 동안 useState는 현재 state를 줍니다. Hook 이름이 항상 use로 시작하는 이유도 있습니다.

  • memory cells라고 불리는 컴포넌트들의 list가 react에 존재하고, 자체적인 linking을 통해 useState와 컴포넌트가 this 키워드 없이 연결된다.

  • state는 컴포넌트가 다시 렌더링되어도 그대로 유지된다. 이것은 class의 this.setState와 유사하지만, 이전 state와 새로운 state를 합치지 않는다는 차이점이 있다.

const [fruit, setFruit] = useState(‘banana’);

  • 위 자바스크립트 문법은 “배열 구조 분해”라고 하고, fruit과 setFruit, 총 2개의 값을 만들고 있습니다.
    https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
    구조 분해 할당
    구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.
    developer.mozilla.org

3. useEffect

  • Effect Hook을 사용하면 함수 컴포넌트에서 side effect를 수행할 수 있습니다. 데이터 가져오기, 구독(subscription) 설정하기, 수동으로 리액트 컴포넌트의 DOM을 수정하는 것까지 이 모든 것이 side effects입니다. 리액트의 class 생명주기 메서드에 친숙하다면, useEffect Hook을 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋습니다.
    => 간단하게 말하자면 렌더링 이후에 비동기로 처리되어야 하는 부수적인 코드를 말한다.

  • useEffect가 하는 일은 무엇일까요? useEffect Hook을 이용하여 우리는 리액트에게 컴포넌트가 렌더링 이후에 어떤 일을 수행해야하는 지를 말합니다. 리액트는 우리가 넘긴 함수를 기억했다가(이 함수를 ‘effect’라고 부릅니다) DOM 업데이트를 수행한 이후에 불러낼 것입니다.
    // 화살표 함수 부분을 effect라고 한다
    useEffect(() => { document.title = You clicked ${count} times; });

  • useEffect를 컴포넌트 안에서 불러내는 이유는 무엇일까요? useEffect를 컴포넌트 내부에 둠으로써 effect를 통해 count state 변수(또는 그 어떤 prop에도)에 접근할 수 있게 됩니다. 함수 범위 안에 존재하기 때문에 특별한 API 없이도 값을 얻을 수 있는 것입니다. Hook은 자바스크립트의 클로저를 이용하여 리액트에 한정된 API를 고안하는 것보다 자바스크립트가 이미 가지고 있는 방법을 이용하여 문제를 해결합니다.

  • useEffect는 렌더링 이후에 매번 수행되는 걸까요? 네, 기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행됩니다. 마운팅과 업데이트라는 방식으로 생각하는 대신 effect를 렌더링 이후에 발생하는 것으로 생각하는 것이 더 쉬울 것입니다. 리액트는 effect가 수행되는 시점에 이미 DOM이 업데이트되었음을 보장합니다.
    => Effect로 전달하는 함수가 화살표함수이기 때문에 매번 함수가 다른 객체가 되고, 내부에서 사용하는 state가 최신임을 보장한다.
    => 브라우저 화면이 다 그려질 때까지 useEffect가 지연된다. 새로운 렌더링이 발생하기 이전에 발생하는 것도 보장된다. 새로운 갱신을 시작하기 전에 이전 렌더링을 항상 완료한다.
    => 게임엔진처럼 오브젝트가 매번 프레임무브 및 렌더링이 일어나지만, fps처럼 실시간 렌더링은 아니고 이벤트가 끝나야 이루어진다.

  • componentDidMount 혹은 componentDidUpdate와는 달리 useEffect에서 사용되는 effect는 브라우저가 화면을 업데이트하는 것을 차단하지 않습니다. 이를 통해 애플리케이션의 반응성을 향상해줍니다. 대부분의 effect는 동기적으로 실행될 필요가 없습니다. 흔하지는 않지만 (레이아웃의 측정과 같은) 동기적 실행이 필요한 경우에는 useEffect와 동일한 API를 사용하는 useLayoutEffect라는 별도의 Hook이 존재합니다.

  • 위에서 정리(clean-up)가 필요하지 않은 side effect를 보았지만, 정리(clean-up)가 필요한 effect도 있습니다. 외부 데이터에 구독(subscription)을 설정해야 하는 경우를 생각해보겠습니다. 이런 경우에 메모리 누수가 발생하지 않도록 정리(clean-up)하는 것은 매우 중요합니다.
    => useEffect의 반환값에 들어가는 함수를 통해 정리할 수 있으며, 소멸자같은 개념으로 이해한다.

  • 리액트가 effect를 정리(clean-up)하는 시점은 정확히 언제일까요? 리액트는 컴포넌트가 마운트 해제되는 때에 정리(clean-up)를 실행합니다. 하지만 위의 예시에서 보았듯이 effect는 한번이 아니라 렌더링이 실행되는 때마다 실행됩니다. 리액트가 다음 차례의 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect 또한 정리하는 이유가 바로 이 때문입니다.

  • 관심사를 구분하려고 한다면 Multiple Effect를 사용합니다.
    => 이전 클래스 컴포넌트에서는 DidMount, Unmount 내에서 여러 로직의 처리가 섞였었다.

  • 모든 렌더링 이후에 effect를 정리(clean-up)하거나 적용하는 것이 때때로 성능 저하를 발생시키는 경우도 있습니다.
    useEffect(() => {
    document.title = You clicked ${count} times;
    }, [count]); // count가 바뀔 때만 effect를 재실행합니다.

  • effect를 실행하고 이를 정리(clean-up)하는 과정을 (마운트와 마운트 해제 시에)딱 한 번씩만 실행하고 싶다면, 빈 배열([])을 두 번째 인수로 넘기면 됩니다. 이는 의존성 배열의 작동 방법을 그대로 따라서 사용하는 것일 뿐이며 특별한 방법인 것은 아닙니다.

4. hooks 유의사항

  • 최상위(at the Top Level)에서만 Hook을 호출해야 합니다 반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 마세요. 대신 항상 React 함수의 최상위(at the top level)에서 Hook을 호출해야 합니다. 이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장됩니다.

  • React는 어떻게 특정 state가 어떤 useState 호출에 해당하는지 알 수 있을까요? 정답은 React가 Hook이 호출되는 순서에 의존한다는 것입니다. 모든 렌더링에서 Hook의 호출 순서는 같기 때문에 예시가 올바르게 동작할 수 있습니다.

  • 오직 React 함수 내에서 Hook을 호출해야 합니다 Hook을 일반적인 JavaScript 함수에서 호출하지 마세요.
  • 사용자 정의 Hook은 이름이 use로 시작하는 자바스크립트 함수입니다. 사용자 Hook은 다른 Hook을 호출할 수 있습니다.
  • 사용자 정의 Hook의 이름은 “use”로 시작되어야 하나요? 네 그렇습니다. 이 관습은 아주 중요합니다. 이를 따르지 않으면 특정한 함수가 그 안에서 Hook을 호출하는지를 알 수 없기 때문에 Hook 규칙의 위반 여부를 자동으로 체크할 수 없습니다.
    => use라는 컨벤션을 통해서 자바스크립트 함수에 불과한 커스텀 훅에서도 useState나 effect를 쓸 수 있다

심도깊게 공부한 블로그 글이 있길래 첨부해본다.
이렇게 열심히 공부해야하는데…

https://velog.io/@gwak2837/React-Hooks%EC%9D%98-%EC%9D%B4%ED%95%B4
이미지 썸네일 삭제

React Hooks 이해하기 (1)
React는 v16.8부터 컴포넌트 상태와 컴포넌트 생명주기를 관리할 수 있는 API인 Hook을 제공하고 있다. Hook을 사용하면 함수 컴포넌트에서도 클래스 컴포넌트처럼 상태를 저장할 수 있고 컴포넌트 생명주기에 관여할 수 있다.
velog.io

https://velog.io/@gwak2837/React-Hooks-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-2
이미지 썸네일 삭제

React Hooks 이해하기 (2)
앞서 살펴본 기본적인 3가지 hook 이외에도 React에는 다양한 hook이 존재한다. useReducer useState와 역할은 비슷한데, useState가 제공하는 기능 이외에도 상태 업데이트 로직을 외부 함수로 분리할 수 있는 기능을 제공한다. 그래서 상태를
velog.io

<지엽적인거 정리=""> useMemo 쓸만한 일없나? 리렌더링으로 객체가 새로 생성되서 useEffect가 실행되는 현상을 막을 수 있음 자바스크립트 함수는 객체다. 리렌더링되서 다시 함수가선언되면 또 함수가 생성된다. useMemo의 함수버전이 useCallback이다. 함수를 다시 만들지 않는다. useRef는 리렌더링을 유발하지 않고 값을 저장한다. 값이 변경되면 화면이 다시 그려지고 그거때매 다시 값이 변경되고 무한루프에 빠지는 현상을 막는데 유용하다. const inputRef = useRef() 이렇게하면 특정 태그를 레퍼런스할 수 잇다. inputRef.current.focus() 이런식으로 직접 조작할 수도 있다. https://www.youtube.com/channel/UCDfJ2rNZsANU6ZNYNlzDJ5Q/videos 별코딩에 잘정리된 영상있음