Hooks의 개념과 useState, useEffect
Function Component
- state 사용 불가
- Lifecycle에 따른 기능 구현 불가
- Hooks
Class Component
- 생성자에서 state를 정의
- setState() 함수를 통해 stae 업데이트
- Lifecycle methods 제공
React의 hook도 마찬가지로 React의 state와 생명주기 기능의 갈고리를 걸어 원하는 시점에 정해진 함수가 실행되도록 만든 것.
→ 이 때 실행되는 함수: hook
hook은 use로 시작함.
개발자가 직접 커스텀 훅을 만들어서 사용할 수 있다.
- 커스텀 훅: 이름을 마음대로 정할 수는 있지만 앞에 use를 붙여 hook이라는 걸 나타내 줘야 함.
useState()
state를 사용하기 위한 hook
함수 컴포넌트에서는 기본적으로 state를 제공X
- state를 사용하고 싶으면 useState hook을 사용해야 함
import React, {useState} from "react";
function Counter(props){
var count = 0;
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={()=>count++}>
클릭
</button>
<div>
);
- 이 코드는 버튼 클릭을 통해 counter 값을 증가시킬 수는 있지만 재렌더링 되지 않아 화면에 표시되지 않음
- useState를 사용하여 상태 업데이트
useState() 사용법
const [변수명, set함수명] = useState(초기값);
- useState를 호출할 때는 파라미터로 선언할 state의 초기값이 들어감.
- 이렇게 하면 return 값으로 배열이 나옴.
- 첫 번째 항목: state로 선언된 변수,
- 두 번째 항목: state의 set 함수
import React, {useState} from "react";
function Counter(props){
const [count, setCount] = useState(0);
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={()=>setCount(count+1)}>
클릭
</button>
<div>
);
- 버튼이 눌렸을 때 setCount 함수를 호출해서 카운트를 1 증가시킨다.
- 카운트값이 변경되면 컴포넌트가 재렌더링 되면서 화면에 새로운 카운트 값이 표시된다.
클래스 컴포넌트에서의 state와 차이점
클래스 컴포넌트에서는 setState 함수 하나를 사용해서 모든 state 값을 업데이트
함수 컴포넌트에서 useState는 변수 각각에 대해 set함수가 따로 존재한다.
useEffect()
리액트의 함수 컴포넌트에서 사이드 이펙트를 실행할 수 있게 해주는 hook
- 리액트에서 side effect: 효과 혹은 영향
- 예) 서버에서 데이터 받아오기, 수동에서 DOM 변경
- useEffect() 훅만으로 생명주기함수와 동일한 기능을 수행
useEffect() 사용법
useEffect(이펙트 함수, 의존성 배열):
- 첫 번째 파라미터: 이펙트 함수
- 두 번째 파라미터: 의존성 배열
- 의존성 배열: 이 이펙트가 의존하고 있는 배열, 배열 안에 있는 변수 중 하나라도 변경 되었을 때 effect함수가 실행됨.
- 이펙트 함수는 처음 컴포넌트가 렌더링 된 이후와 업데이트로 인한 재렌더링 이후에 실행
useEffect(이펙트 함수, []):
- effect function이 mount, unmount 시에 단 한 번씩만 실행됨
- props나 state에 있는 어떤 값에도 의존하지 않는것이 됨. → 여러번 실행X
useEffect(이펙트 함수):
- 의존성 배열 생략하면 컴포넌트가 업데이트 될 때마다 호출 됨
예제 코드)
import React, {useState, useEffect} from "react";
function Counter(props){
const [count, setCount] = useState(0);
useEffect(()=>{
document.title = `you clicked ${count} times`;
});
return (
<div>
<p>총 {count}번 클릭했습ㄴ디ㅏ.</p>
<button onClick={()=> setCount(count+1)}>
클릭
</button>
</div>
);
}
- useEffect 안에 있는 이펙트 함수에서는 브라우저에서 제공하는 API를 사용해서 document의 title 업데이트
- 의존성배열X → react는 DOM이 변경 된 이후에 해당 이펙트 함수를 실행하라는 의미로 받아들임
- 컴포넌트가 매번 렌더링 될 때마다 이펙트가 실행
- 이펙트는 함수 컴포넌트 안에서 선언되기 때문에 해당 컴포넌트의 props와 state에 접근할 수 있음.
- 이 코드에서는 count 활용
useEffect 훅은 하나의 컴포넌트의 여러 개를 사용할 수 있음.
useEffect 요약
useEffect(()=>{
// 컴포넌트가 마운트 된 이후,
// 의존성 배열에 있는 변수들 중 하나라도 값이 변경되었을 때 실행됨
// 의존성 배열에 빈 배열([])을 넣으면 마운트와 언마운트시에 단 한번씩만 실행됨
// 의존성 배열 생략 시 컴포넌트 업데이트 시마다 실행됨
return ()=>{
// 컴포넌트가 마운트 해제되기 전에 실행됨
...
}
}, [의존성 변수1, 의존성 변수2, ...]);
useMemo
memoized value를 리턴하는 hook
- memoization: 최적화를 위해 사용되는 개념, 연산량이 많이 드는 함수의 호출 결과를 저장해 두었다가 같은 입력 값으로 함수를 호출하면 새로 함수를 호출하지 않고 이전에 저장해 놨던 호출 결과를 바로 반환하는 것.
- 함수 결과 받는 시간 단축, 불필요한 중복 연산X → 자원을 아낄 수 있음
- memorized value: memoization 된 결과 값
useMemo() 사용법
const memoizedValue = useMemo(
() => {
//연산량이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성변수2]
);
- 의존성 배열에 들어있는 변수가 변했을 경우에만 새로 create함수를 호출하여 결과값을 반환
- 그렇지 않은 경우에는 기존함수의 결과값을 반환
- 컴포넌트가 다시 렌더링 될 때마다 연산량이 높은 작업을 반복하는 것을 피할 수 있음.
- 렌더링이 일어나는 동안 실행돼서는 안될 작업을 useMemo 함수에 넣으면 안됨.
- 예) useEffect 사이드 이펙트
const memoizedValue = useMemo(
()=>computeExpensiveValue(a, b)
);
의존성 배열을 넣지 않을 경우, 매 렌더링마다 create 함수가 실행 됨
→ 의존성 배열 넣지 않으면 아무런 의미가 없음
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(a, b);
},
[]
);
의존성 배열이 빈 경우, 컴포넌트 마운트 시에만 호출 됨
- 마운트 이후에는 값 변경X
useCallback()
useMemo() hook과 유사하지만 값이 아닌 함수를 반환
의존성 배열의 값이 바뀐 경우에만 함수를 새로 정의해서 리턴함
useCallback() 사용법
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
- 파라미터로 받는 함수를 callback이라고 부름.
- 의존성 배열에 있는 변수 중 하나라도 변경되면 memoization된 콜백함수를 반환
- useMemo hook과 동일
useCallback(함수, 의존성 배열);
useMemo(()=> 함수, 의존성 배열);
동일한 역할을 하는 두 줄의 코드
- useCallback 훅을 사용하지 않고 컴포넌트 내에 함수를 정의한다면 매번 렌더링이 일어날 때마다 함수가 새로 정의됨
- useCallback을 사용하여 특정 변수의 값이 변한 경우에만 함수를 다시 정의
useRef()
레퍼런스를 사용하기 위한 훅
- 리액트에서 레퍼런스: 특정 컴포넌트에 접근할 수 있는 객체
- useRef은 이 레퍼런스 객체를 반환
- 레퍼런스 객체에는 current 속성이 있음 → 현재 레퍼런스 하고있는 엘리먼트를 의미
useRef() 사용법
const refContainer = useRef(초기값);
파라미터로 초기값을 넣으면 해당 초기값으로 초기화된 레퍼런스 객체를 반환
예시) 초기값이 null 이면 current의 값이 null인 레퍼런스 객체 반환
ref속성과 기능은 비슷하지만 useRef hook은 클래스의 instance 필드를 사용하는 것과 유사하게 다양한 변수를 저장할 수 있다는 것이 장점
- useRef hook이 일바적인 자바스크립트 객체를 리턴하기 때문
useRef() Hook은 내부의 데이터가 변경되었을 때 별도로 알리지 않는다.
- currnet 속성을 변경한다고 해서 재렌더링X
Callback ref
돔노드의 변화를 알기 위한 가장 기초적인 방법
callback-ref 방식을 사용하게 되면 자식 컴포넌트가 변경되었을 때 알림을 받을 수 있고 다른 정보들을 업데이트 할 수 있음.
Hook의 규칙과 Custom Hook 만들기
Hook 규칙
- Hook은 무조건 React함수 컴포넌트의 최상위 레벨에서만 호출
- 반복문이나 조건문 또는 중첩된 함수들 안에서 Hook을 호출하면 안됨
- hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 함.
- 리액트 함수 컴포넌트에서만 hook을 호출해야 함.
eslint-plugin-react-hooks: hook의 규칙을 따르도록 강제해주는 플러그인
Custom Hook 만들기
custom hook은 이름이 use로 시작하고 내부에서 다른 hook을 호출하는 하나의 자바스크립트 함수
그냥 단순한 함수와 비슷하다
- 파라미터로 무엇을 받을지, 어떤 것을 리턴해야 할지를 개발자가 직접 정함
custom hook의 이름은 꼭 use로 시작?
이름이 use로 시작하지 않으면 특정 함수의 내부에서 hook을 호출하는지 알 수 없음
→ hook의 규칙 위반 여부를 자동으로 확인 불가
같은 커스텀 훅을 사용하는 두 개의 컴포넌트는 state를 공유?
여러 개의 컴포넌트에서 하나의 custom hook을 사용할 때 컴포넌트 내부에 있는 모든 state와 effects는 전부 분리되어 있음\
커스텀 훅은 어떻게 state를 분리?
리액트 컴포넌트는 각 커스텀 훅 호출에 대해서 분리된 state를 얻게 됨
각 커스텀 훅의 호출은 완전히 독립적임.
hook들 사이에서 데이터를 공유하는 방법
예시코드
const [userId, setUserId] = useState(1);
const isUserOnline = useUserStatus(userId);
useState Hook을 사용해서 userId라는 state를 만듦.
userId는 useUserStatus 훅의 파라미터로 들어감.
setUserId 함수를 통해 유저 아이디가 변경될 때마다 useUserStatus Hook은 이전에 선택된 사용자를 취소하고 새로 선택된 사용자를 업데이트
'Develop > FE' 카테고리의 다른 글
| [React] Lists and Keys & Forms & Lifting State Up (1) | 2024.10.08 |
|---|---|
| [React] Handling Events & Conditional rendering (1) | 2024.10.03 |
| [React] State and Lifecycle (1) | 2024.09.19 |
| [React] Components와 Props (1) | 2024.09.09 |
| [React] Rendering elements (2) | 2024.09.08 |