
기존엔 Class Componet만 Sate를 정의해서 사용할 수 있었지만, Hook을 사용하면 Function Component를 사용하면서도 State를 구현할 수 있다.

State를 사용하기 위한 Hook.
변수 1개에 setState함수 1개가 매칭되는 느낌
const [변수명, set함수명] = useState(0);
예시
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>
);
}
리액트에서 Effect란?
서버에서 데이터를 받아오거나 수동으로 DOM을 변경하는 경우(=setState)처럼
1. 다른 컴포넌트에 영향을 미칠 수 있고
2. 렌더링 중에는 작업이 완료될 수 없는 상황을 말한다.
Class Component에서 제공하는 생명주기 함수를 사용할 수 있게 해준다.
// 정의
useEffect(이펙트 함수, 의존성 배열);
// 배열이 비어있으면,
// 이펙트 함수가 mount, unmount 시에 단 한번씩만 실행된다.
useEffect(이펙트 함수, []);
// 배열을 생략하면,
// Component가 업데이트 될 때마다 이펙트 함수가 실행된다.
useEffect(이펙트 함수);
예시
import React, { useState, useEffect } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// componentDidMount, componentDidUpdate와 비슷하게 작동함
useEffect(() => {
// 브라우저 API를 사용해서 document의 title을 업데이트함
document.title = 'You clicked ${count} times';
});
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count+1)}>
클릭
</button>
</div>
);
}
Memoized Value를 Return하는 함수
Memoization
최적화와 관련된 개념.
캐시(Cache)처럼 비용이 많이 드는 함수값을 저장해두고 다음에 호출되었을 때 사용하는 것
연산량이 높은 작업을 줄일 수 있다.
useMemo()가 호출됐을 때, 의존성 배열의 변수가 이전과 다르다면 Memoized Function을 실행하고, 같다면 이전 결과를 반환한다.
// 기본
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
// 만약 배열을 생략한다면,
// 렌더링마다 함수가 실행된다.
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
}
);
// 만약 배열이 비어있다면,
// Component가 생성될 때만 실행되고, 이후엔 실행되지 않는다.
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[]
);
주의
useMemo()는 렌더링 중에도 실행될 수 있다. 따라서 렌더링 중에 실행되면 안 되는 기능 (ex. useEffect())는 useMemo()에서 사용하면 안 된다.
useMemo()와 비슷하지만, 반환값이 함수임
// 기본
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
Reference
특정 컴포넌트에 접근할 수 있는 객체
const refContainer = useRef(초깃값);
**예시
function TextInputWithFocusButton(props) {
const inputElem = useRef(null);
const onButtonClick = () => {
inputElem.current.focus();
};
return (
<div>
<input ref={inputElem} type="text" />
<button onClick={onButtonClick}>
Focus the input
</button>
</div>
);
}
Hook은 반복문, 조건문, 중첩된 함수 내에서 실행되어선 안 된다. 이에 따라 Hook은 렌더링 시, 매번 같은 순서로 호출되어야 한다. 그래야, Component의 State와 Effect를 올바르게 관리할 수 있다.
function MyComponent(props) {
const [name, setName] = useState('Inje');
if (name !== '') {
useEffect(() => {...});
}
...
}
Hook은 리액트 컴포넌트와 커스텀 컴포넌트에서만 사용할 수 있고, 일반 JS에서 사용하면 안 된다.
eslint-plugin-react-hooks
위와 같은 Hook의 규칙을 개발 단계에서 따르게끔 설정하는 플러그인이다.
원래 eslint는 JS 코드의 문제 패턴을 식별하기 위한 정적 코드 분석 도구이다.
여러 컴포넌트에서 반복적으로 사용하는 로직을 따로 작성하여 재사용하기 위한 Hook이다.
아래 예시는 UserStatus()와 UserListItem()의 Hook이 같다.
import React, { useState, useEffect } from "react";
function UserStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ServerAPI.subscripbeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
}
if (isOnline === null) {
return '대기중...';
}
return isOnlie ? '온라인' : '오프라인';
}
function UserListItem(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ServerAPI.subscripbeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
}
return (
<li style={{color: isOline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
이럴 경우, 중복되는 코드를 Custom Hook으로 따로 만들어 사용할 수 있다.
이를 통해 다음과 같이 코드를 수정할 수 있다.
import { useState, useEffect } from "react";
function useUserStatus(userId) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ServerAPI.subscripbeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
}
return isOline;
}
function UserStatus(props) {
const isOnlie = useUserStatus(props.user.id);
if (isOnline === null) {
return '대기중...';
}
return isOnlie ? '온라인' : '오프라인';
}
function UserListItem(props) {
const isOnlie = useUserStatus(props.user.id);
return (
<li style={{color: isOline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
아래 코드는 유저를 선택하였을 때, 그 유저가 온라인인지 아닌지를 판단하여 색상이 바뀌는 컴포넌트이고, useUserStatus()의 파라미터로 userId가 들어가 있다. 따라서, setUserId()로 userId를 바꾸면, 이를 useUserStatus()가 감지하여 isUserOnline을 업데이트하게 된다.
const userList = [
{ id: 1, name: 'Inje' },
{ id: 2, name: 'Mike' },
{ id: 3, name: 'steve' }
];
function ChatUserSelector(props) {
const [userId, setUserId] = useState(1);
const isUserOnline = userUserStatus(userId);
return (
<div>
<Circle color={isUserOnline ? 'green' : 'red'} />
<select
value={userId}
onChange={(event) => setUserId(Number(event.target.value))}>
{userList.map((user) => (
<option key={user.id} value={user.id}>
{user.name}
</option>
))}
</select>
</div>
);
}