학습목표
•
리액트 고급 훅에 대해 탐구해보기
•
리액트 커스텀 훅 만들어보기
•
React memo, useCallback을 사용해서 앱 최적화하기
•
리액트에서의 불변성에 대해 알기
◦
state와의 관련성에 대해서 확인하기
훅이란?
리액트 훅
: 함수형 컴포넌트에서도 직관적인 함수를 이요하여 작업할 수 있게 만든 기능
•
상태 관련 로직 추상화 가능
•
재사용, 테스트 가능
•
계층의 변화 없이 상태 관련 로직 재사용
사용 규칙
1.
최상위 레벨에서만 호출 가능
→ 반복문, 조건문, 중첩함수 내부에서 호출 X
2.
리액트 함수 컴포넌트 내에서만 호출 가능
훅의 종류
useState()
const [state, setState] = useState(initialState);
JavaScript
복사
•
상태를 설정할 때 사용
◦
이 훅을 통해 설정된 상태는 컴포넌트가 리렌더링 되어도 유지됨 !!
◦
하나의 컴포넌트에 여러 개의 상태 정의 가능
하나의 상태를 여러 컴포넌트에서 사용하는 경우 …
→ 매번 props를 전달해야하는 불필요한 코드 중복 발생
→ 이 경우에는 Redux나 Recoil 등의 전역 상태 라이브러리 이용 권장
•
initialState : 초기 상태
•
state : 현재 상태
•
setState : 상태를 설정하는 함수
useEffect()
useEffect(callback, dependency);
JavaScript
복사
•
side effect를 발생하는 작업 수행
◦
side effect: 다른 컴포넌트에 영향 O, 렌더링 과정에서는 구현 X
ex) 컴포넌트 안에서 데이터 가져오기, DOM을 직접 조작하기
•
callback : side effect가 발생하는 작업 수행
useEffect()의 callback 함수에서 async를 사용하면 안됨 !!
→ async를 사용하면 cleanup 함수가 아닌 promise 반환, 오류 발생
•
반환값 : 정리 작업을 수행하는 cleanup 함수
•
dependency
◦
아무것도 넣지 않았을 때 : 매 렌더링 시마다 콜백 함수 실행
◦
빈 배열을 넣는 경우 : 마운트 시 콜백 함수 실행, 언마운트 시 cleanup 함수 실행
▪
마운트 시의 state값과 props값이 언마운트 때까지 유지
▪
컴포넌트가 가장 처음 렌더링 될 때만 실행
◦
특정 값을 담은 배열을 넣었을 때 : 특정 값이 업데이트 되었을 때만 실행
useEffect(() => {
console.log('useEffect');
}, [state]);
JavaScript
복사
useContext()
코드 보기
•
인자로 context 객체를 받아 객체의 현재값 반환
•
context 객체는 useContext가 호출된 컴포넌트에서 가장 가까운 <Context.Provider>의 props로 전달된 value에 따라 값 변경됨
◦
값 변경될 때마다 컴포넌트 리렌더링
useReducer()
const [state, dispatch] = useReducer(reducer, initialState, init);
JavaScript
복사
•
useState()의 대체 함수
◦
useState()에 비해 유동적으로 상태 관리
•
다양한 컴포넌트 상황에 따라 상태 값 설정
•
상태 관련 로직을 컴포넌트 밖으로 꺼낼 수 있음
코드
•
reducer 함수 : state(현재 상태)와 action(행동)을 인자로 받음
•
initialState : 상태의 초기값
•
init 함수 : 상태 초기화 함수
•
반환값 : state(현재 상태), dispatch(액션을 발생시키는 함수)
•
작동 원리
1.
버튼을 클릭하면 dispatch 함수에 의해 각 버튼마다 다른 액션 객체 설정됨
2.
dispatch가 reducer를 호출, 주어진 액션 객체 값에 따라 상태 변경
이때 reducer 함수에서 상태 변경이 이루어질 때 반드시 불변성을 지켜야함 !!
useRef()
const ref = useRef(initialValue);
JavaScript
복사
•
함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 함
ref란?
: 리액트에서 DOM을 선택해 직접 접근할 때 사용
ex) HTML에서 특정 id값에 해당하는 DOM 요소에 스타일을 따로 적용
ex) 특정 input에 focus 주기, 스크롤 박스 조작, canvas 요소에 그림 그리기 등 ..
•
변경 가능한 ref 객체 반환
◦
이 객체는 컴포넌트의 전 생애주기 동안 유지됨
◦
current 속성에 접근하여 현재 가리키는 객체에 접근 ex) ref.current
◦
가변 값을 유지하기 편리함
useCallback()
const memoizedCallback = useCallback(callback, dependency);
JavaScript
복사
•
렌더링 최적화에 사용
•
dependency의 값이 바뀌면 callback을 새로 생성하여 반환
함수를 반환
useMemo()
const memoizedValue = useMemo(callback, dependency);
JavaScript
복사
•
연산 최적화에 사용
•
dependency 내 값이 바뀔 때에만 callback 실행하여 구한 값을 반환
숫자, 문자열, 배열, 객체 등의 값을 반환
커스텀 훅
•
여러 컴포넌트에서 비슷한 기능을 공유할 때 사용
•
로직 재사용하기 위해서 사용
◦
이때 재사용된 훅들은 모두 독립적임 !!
만드는 과정
1.
src 디렉토리에 hooks라는 디렉토리 생성
2.
use라는 키워드로 시작하는 파일 생성
3.
그 안에서 Hooks를 사용하여 원하는 기능을 구현
•
주로 중복이 되는 로직 넣음
4.
컴포넌트에서 사용하고 싶은 값들을 반환
나도 만들었다
1.
useTodos.js
코드
•
App.js에서 해당 훅을 사용함
const [todos, onAdd, onDel, onToggle] = useTodos(loadTodos());
JavaScript
복사
◦
localStorage에서 가져온 값이 초기값임 (initialValue)
2.
useInput.js
코드
•
TodoForm.js 에서 해당 훅을 사용함
const [text, onChange, onSubmit] = useInput('', onAdd);
JavaScript
복사
◦
onAdd 함수까지 매개변수로 받아와서 훅 내부에서 사용함
앱 최적화
컴포넌트의 리렌더링 조건
•
부모에서 전달받은 props가 변경될 때
•
부모 컴포넌트가 리렌더링 될 때
•
자신의 state가 변경될 때
→ 리렌더링 횟수를 줄임으로써 최적화 !!
최적화 방법
•
useMemo
◦
어떤 함수가 값을 리턴하는데 많은 시간을 소요할 때
◦
리턴되는 값을 memoize
•
React.memo
◦
hook이 아니기 때문에 클래스형 컴포넌트에서도 사용 가능
◦
컴포넌트의 props가 바뀌지 않았다면, 리렌더링하지 않도록 설정
◦
콜백함수를 이용해 메모이제이션 적용 여부도 판단 가능
•
useCallback
◦
리렌더링마다 함수 새로 생성
◦
함수 선언을 memoize
•
자식 컴포넌트의 props로 객체를 변형하지 않고 넘겨주기
안좋은 예시
좋은 예시
→ state를 하위컴포넌트에 넘겨주어 필요한 데이터 가공은 하위컴포넌트에서 수행
•
컴포넌트를 매핑할 때 key값으로 index를 사용하지 않는다
◦
어떤 배열에 중간에 어떤 요소가 삽입될 때 ..
•
useState의 함수형 업데이트
코드
•
Input에 onChange 최적화
코드
불변성
: 값이나 상태를 변경할 수 없는 것
•
메모리 영역에서 값을 변경할 수 없음
•
원시타입 & Object 타입 모두 불변성을 지키고 있음 !
React에서의 불변성
•
효율적인 상태(state) 업데이트
◦
상태 업데이트 할 때 얕은 비교를 수행 (참조값만 비교)
◦
배열/객체를 새로 생성해서 새로운 참조값을 만들어서 상태 업데이트
ex) spread operator, map, filter, slice, reduce
•
사이드 이펙트 방지
◦
원본데이터를 직접 수정하지 X
◦
예상치 못한 오류 사전에 방지
참고자료
훅이란?
훅의 종류
커스텀 훅
앱 최적화
불변성