스터디/모던 리액트 Deep Dive(24.07-24.10)

3.1 : 리액트 훅 (useState, useEffect, useMemo, useCallback)

minseokiim 2024. 8. 27. 18:09

1. useState

함수 컴포넌트 내부에서 상태를 정의하고, 이 상태를 관리할 수 있게 해주는 훅

아무런 값을 넘겨주지 않으면 초기값은 undefined

반환값은 배열, 첫 번째 원소로 state 값 자체를 사용하고 두 번째 원소인 setState함수를 통해 해당 state값 변경 가능

실제 리액트 코드에서는 useReducer로 표현되어 있음.

 

💫 useState를 사용하지 않고 상태값을 함수 내부에서 변수 사용해 관리하면 동작하지 않는 이유

: 리액트의 렌더링은 함수 컴포넌트의 반환 결과물인 return 의 값을 비교해서 실행.

리액트의 함수 컴포넌트는 렌더링될 때마다 함수가 다시 실행 되고, 그 때마다 함수 내부에서 선언된 변수들은 매번 새롭게 초기화 되므로 렌더링이 발생할 때마다 해당 변수가 초기화되어 이전 상태를 잃는 것.

 

💫 console.log(value); 와 console.log(value()); 결과가 다른 이유

: console.log(value)는 함수 자체를 출력,  console.log(value()) 는 함수의 반환값을 출력

 

💫 리액트에서 클로저를 사용하는 이유

useState에 선언된 함수 setState가 함수 실행 종료된 후에도 state계속 참조할 수 있게 함.

 

💫 게으른 초기화

useState에서 원시값을 넣지 않고, 특정한 값을 넘기는 함수를 인수로 넣는 것

state가 처음 만들어질 때만 사용되고, 리렌더링이 발생되면 무시.

초깃값이 무거운 연산 또는 복잡한 값을 포함할 때 사용

ex. storage에 접근, 배열에 대한 접근, 초기값 계산 연산을 위해 함수 호출이 필요한 경우

 

 

2. useEffect

첫 번째 인수로 콜백(부수효과 발생할 함수), 두 번째 인수로 의존성 배열을 받고, 의존성 배열이 변경되면 콜백을 실행.

의존성 배열은 빈배열, 생략 모두 가능

 

💫 useEffect가 의존성 배열이 변경된 것을 알고 실행하는 원리

함수 컴포넌트는 매번 함수를 실행해 렌더링을 수행,

렌더링 할 때마다 의존성에 있는 값을 보며 의존성의 값이 전과 하나라도 다르면 부수효과를 실행

 

💫 클린업 함수

리액트는 컴포넌트가 업데이트될 때마다 useEffect를 다시 실행

-> 이전의 useEffect에서 설정한 것들을 제거하지 않으면 문제 발생 위험

 

1. 이벤트를 등록하고 지울때 사용

2. 비동기 작업을 취소할 때 사용

클린업 함수는 이전 state를 참조해 새로운 값과 함께 렌더링 된 후 실행

 

💫 useEffect에 이벤트를 추가했을 때 클린업 함수에서 지워야 하는 이유

컴포넌트가 계속해서 렌더링될 때마다 동일한 이벤트 리스너가 계속해서 추가되면, 동일한 이벤트에 대해 여러 핸들러가 중복해서 실행되는 문제가 발생

함수 컴포넌트 useEffect는 그 콜백이 실행될 때 마다 이전의 클린업 함수가 존재하면, 클린업 함수를 실행한 후 콜백을 실행함. 따라서 이벤트를 추가하기 전에 등록했던 이벤트 핸들러를 삭제하는 코드를 클린업 함수에 추가

-> 특정 이벤트 핸들러가 무한히 추가되는 것을 막을 수 있음

 

 

💫 의존성 배열

빈배열 : 최초 렌더링 직후에 실행되고 이후 실행되지 않음

아무것도 넘겨주지 않음 : 렌더링 할 때 마다 실행 -> 컴포넌트 렌더링 확인 용도

 

의존성 배열의 비교는 얕은 비교

 

💫 의존성 배열 주의

1. 의존성 배열을 넘기지 않은 채 콜백함수 내에서 특정 값을 사용하는 경우

2. 익명함수를 넘기는 것 -> 코드 추적이 어려워짐

3. 크기 -> 부수효과가 커질 수록 성능에 악영향

4. 불필요한 외부 함수 줄이기

 

+ 바로 비동기 함수 넣을 수 없는 이유 : 응답 속도에 영향 받기 때문,

하지만 즉시 실행 비동기함수나 useEffect 내부에서 선언한 비동기함수는 가능

-> 대신 클린업 함수에서 처리해주는 것을 추천

 

 

 

3. useMemo

비용이 큰 연산에 대한 결과를 저장하고 저장된 값 반환

첫 번째 인수는 어떤 값을 반환하는 함수, 두 번째 인수는 해당 함수가 의존하는 값의 배열

렌더링 발생 시, 의존성 배열 값 변경되었을 경우에만 첫 번째 인수의 함수 실행하고, 값을 반환해 기억.

의존성 배열 값 변경 안되었으면 함수 재실행 x, 이전에 기억한 값 반환

값 뿐만 아니라 컴포넌트도 감쌀 수 있음, 하지만, 컴포넌트는 React.Memo 사용이 더 효과적.

 

 

💫 React.memo와 useMemo 차이?

- React.memo컴포넌트의 불필요한 렌더링 방지가 목적,

부모 컴포넌트가 다시 렌더링될 때마다 자식 컴포넌트도 다시 렌더링되는데,

React.memo를 사용하면, 컴포넌트의 props가 이전 렌더링과 비교하여 변하지 않았을 경우, 그 컴포넌트를 다시 렌더링하지 않음.

 

- useMemo복잡한 계산의 결과를 메모이제이션 -> 불필요한 재계산을 방지하는 것이 목적,

함수 컴포넌트 내에서 특정 의존성 배열에 있는 값들이 변경되지 않는 한, 이전에 계산된 결과를 반환

 

 

💫 useMemo를 통해 컴포넌트 메모이제이션 하는 것과 React.memo 사용의 차이?

- useMemo는 컴포넌트의 반환값(JSX)을 메모이제이션

계산된 값이나 JSX 요소가 다시 계산되지 않도록.

-> 특정 의존성이 변경되지 않는 한, 리액트가 이전에 계산된 JSX를 재사용

 

- React.memo는 컴포넌트 자체를 메모이제이션

부모 컴포넌트가 다시 렌더링되더라도 props가 변경되지 않으면 해당 컴포넌트는 다시 렌더링되지 않음.

-> 전체 컴포넌트의 렌더링을 최적화

 

 

💫   useMemo를 써도 자식 컴포넌트가 렌더링 되는 이유?

: 리렌더링 될 때 함수가 재생성되기 때문.-> useCallback 사용해주기

 

 

4. useCallback

인수로 넘겨받은 콜백 자체를기억 해서,  함수를 새로 만들지 않고 다시 재사용

첫 번째 인수는 함수, 두 번째 인수는 해당 함수가 의존성 배열

의존성 배열 값 변경 안되었으면 함수 재생성 x

useMemo로 구현 가능(반환문으로 함수 선언문을 반환)

 


💫
 useMemo와 useCallback 차이?

: 메모이제이션 하는 대상이 다름.

useMemo는 변수를 기억, useCallback는 함수를 기억