16 ~ 18장
useEffect | useMemo | useCallback
- 마운트 (처음 나타났을 때)
- 언마운트 (사라질 때)
- 업데이트 ( 특정 props가 바뀔 때)
특정 작업을 처리하는 방법
useEffect를 사용방법
useEffect( 함수 , 의존값이 들어있는 배열(deps))
만약 deps 배열을 비우게 된다면, 컴포넌트가 처음 나타날때에만 useEffect에 등록한 함수가 호출된다.
그리고, useEffect에서는 함수를 반환할 수 있는데, 이를 cleanup 함수라고 부른다.
컴포넌트가 리렌더링 될 때마다 호출이 된다.
참고로 리액트 컴포넌트는 기본적으로 부모 컴포넌트가 리렌더링되면 자식 컴포넌트 또한 리렌더링이 된다. (바뀐 내용이 없다할지라도!!)
물론, 실제 DOM에 변화가 반영되는 것은 바뀐 내용이 있는 컴포넌트에만 해당한다. 하지만 Virtual DOM에는 모든걸 다 렌더링하고 있다는 거다.
나중에는, 컴포넌트를 최적화 하는 과정에서 기존의 내용을 그대로 사용하면서 Virtual DOM에 렌더링하는 리소스를 아낄 수도 있다.
useEffect
마운트 : 처음 나타남
언마운트 : 사라짐구조
첫번째 인자는 함수, 두번째 인자는 배열(주로 deps 라고 칭한다.)이 들어간다.
cleanup 함수
useEffect 안에서 return 할 때 실행된다.
useEffcet의 뒷정리를 한다. -> state에서 값 지울때 실행됨deps
deps 에 특정값을 넣게 되면, 컴퍼넌트가 마운트 될 때, 지정한 값이 업데이트 될 때 useEffect 실행
deps에 값이 없다면 useEffect가 최신 값을 가리키지 않게 된다.
deps에 값이 없다면 컴포넌트가 리렌더링 될 때마다 호출이 된다.
deps에 값을 넣는것을 기본이라고 생각하는게 좋다.
출처 -개발화라리 유튜버님
function countActiveUsers(users) {
console.log('활성 사용자 수를 세는중...');
return users.filter(user => user.active).length;
}
const count = countActiveUsers(users);
<div>활성사용자 수 : {count}</div>
countActiveUsers 함수에서 콘솔에 메시지를 출력하도록 한 이유는, 이 함수가 호출될때마다 우리가 알 수 있게 하기 위함이다.
=> 활성 사용자 수를 세는건, users 에 변화가 있을때만 세야되는건데, input 값이 바뀔 때에도 컴포넌트가 리렌더링 되므로 이렇게 불필요할때에도 호출해서 자원이 낭비된다
Memo = 'Memoized" 의미 = '이전에 계산한 값을 재사용한다' 는 의미
[사용방법]
이 배열 안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 된다.
import React, { useRef, useState, useMemo } from 'react';
const count = useMemo(() => countActiveUsers(users), [users]);
useCallback은 useMemo와 비슷한 Hook이다.
이벤트 핸들러 함수나 api를 요청하는 함수를 주로 useCallback으로 선언한다.
이전에 App.js에서 구현했었던 onCreate, onRemove, onToggle 함수를 확인하면
그래도 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 중요하다!
우리가 나중에 컴포넌트에서 props가 바뀌지 않았다면 Virtual DOM에 새로 렌더링하는 것 조차 하지않고 컴포넌트의 결과물을 재사용하는 최적화 작업 할건데, 이때 함수 재사용 필수다
함수가 매번 재선언되면 하위 컴포넌트는 넘겨 받은 함수가 달라졌다고 인식한다.
컴포넌트는 자신의 state가 변경되거나, 부모에게서 받는 props가 변경되었을 때마다 리렌더링된다. (심지어 하위 컴포넌트에 최적화 설정을 해주지 않으면 부모에게서 받는 props가 변경되지 않았더라도 리렌더링되는게 기본이다 ) 이게 싫으면 => React.memo()
함수는 오로지 자기 자신만이 동일하기 때문에 상위 컴포넌트에서 callback함수를 (같은 함수더라도 ) 재선언한다면 props로 callback 함수를 넘겨 받는 하위 컴포넌트 입장에서는 props가 변경되었다고 인식한다.
import React, { useRef, useState, useMemo, useCallback } const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [users, username, email]);
const onRemove = useCallback(
id => {
// user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// = user.id 가 id 인 것을 제거함
setUsers(users.filter(user => user.id !== id));
},
[users]
);
const onToggle = useCallback(
id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
},
[users]
);
## 주의할 점
- 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 된다는 것 입니다.
- 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없습니다.
- props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 해요.
- 사실, useCallback 은 useMemo 를 기반으로 만들어졌습니다. 다만, 함수를 위해서 사용 할 때 더욱 편하게 해준 것 뿐이지요. 이런식으로도 표현 할 수 있습니다.
우아! 정말 좋은 정보 감사합니다 :)