useState()
useState()
훅을 사용해야 함const [변수명, set 함수명] = useState(초깃값);
import React, { useState } from "react";
function Counter(props) {
var count = 0;
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => count++}>
클릭
</button>
</div>
);
}
useState()
함수를 사용하는 코드로 바꿔보자.setCount()
함수를 호출해서 카운트를 1 증가시키고, count 값이 변경되면 컴포넌트가 재렌더링되면서 화면에 새로운 카운트 값이 표시된다.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>
);
}
useEffect()
componentDidMount()
, componentDidUpdate()
, componentWillUnmount()
와 동일한 기능을 하나로 통합해서 제공함으로써 useEffect()
훅만으로 생명주기 함수와 동일한 기능을 수행할 수 있음.useEffect(이펙트 함수, 의존성 배열);
[]
)을 넣으면 됨.위에서 작성했던 코드에 추가로 useEffect()
훅을 사용하여 클래스 컴포넌트에서 제공하는 componentDidMount()
, componentDidUpdate()
와 같은 생명주기 함수의 기능을 동일하게 수행하도록 만들어보자.
useEffect()
안에 있는 이펙트 함수에서는 브라우저에서 제공하는 API를 사용하여 document title을 업데이트한다.import React, { useState, useEffect } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// componentDidMount, componentDidUpdate와 비슷하게 작동
useEffect(() => {
// 브라우저 API를 사용해서 document의 title 업데이트
document.title = `총 ${count}번 클릭했습니다.`;
});
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count+1)}>
클릭
</button>
</div>
);
}
useEffect()
를 사용하면 리액트는 DOM이 변경된 이후에 해당 이펙트 함수를 실행하라는 의미로 받아들임.componentDidMount()
, componentDidUpdate()
와 동일한 역할을 하는 것!이번에는 componentWillUnmount()
와 동일한 기능을 useEffect()
로 구현하는 방법에 대해 알아보자.
import React, { useState, useEffect } from "React";
function UserStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
};
});
if (isOnline === null) {
return '대기 중...';
}
return isOnline ? '온라인' : '오프라인';
}
useEffect()
에서 먼저 ServerAPI를 사용하여 사용자의 상태를 구독하고 이후 함수를 하나 리턴하는데 해당 함수 안에는 구독을 해지하는 API를 호출하도록 되어 있다.useEffect()
에서 리턴하는 함수는 컴포넌트가 마운트 해제될 때 호출된다.componentWillUnmount()
함수가 하는 역할과 동일하다.userEffect()
훅은 하나의 컴포넌트에 여러 개를 사용할 수도 있다.function UserStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() =› {
document.title =`총 ${count}번 클릭했습니다.`;
});
const [isOnline, setIsOnline] = useState (null);
useEffect(() => {
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
return ( => {
ServerAPI. unsubscribeUserStatus (props.user.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline (status.isOnline);
}
useEffect(() => {
// 컴포넌트가 마운트 된 이후,
// 의존성 배열에 있는 변수들 중 하나라도 값이 변경되면 실행
// 의존성 배열에 빈배열([])을 넣으면 마운트와 언마운트시에 단 한 번씩만 실행
// 의존성 배열 생략 시 컴포넌트 업데이트 시마다 실행
return () => {
// 컴포넌트가 마운트 해제되기 전에 실행됨
}
}, [의존성 변수1, 의존성 변수2, ...]);
useMemo()
useMemo()
훅을 사용하면 컴포넌트가 다시 렌더링될 때마다 연산량이 높은 작업을 반복하는 것을 피할 수 있다.const memoizedValue = useMemo(
() => {
// 연산량이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
useMemo()
로 전달된 함수는 렌더링이 일어나는 동안 실행되기 때문에 일반적으로 렌더링이 일어나는 동안 실행돼서는 안될 작업을 useMemo()
안에 넣으면 안됨.useMemo()
훅에 의존성 배열을 넣지 않는 것은 아무런 의미가 없음useCallback()
useMemo()
훅과 유사한 역할을 하지만 값이 아닌 함수를 반환함const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
useCallback()
훅을 사용하지 않고 컴포넌트 내에 함수를 정의한다면 매번 렌더링이 일어날 때마다 함수가 새로 정의됨useCallback()
훅을 사용하여 특정 변수의 값이 변한 경우에만 함수를 다시 정의하도록 해서 불필요한 반복 작업을 없애줌useRef()
.current
라는 속성이 있는데 이것은 현재 레퍼런스(참조)하고 있는 엘리먼트를 의미const refContainer = useRef(초깃값);
.current
속성을 변경하는 것은 재렌더링을 일으키지 않으므로 ref에 DOM node가 연결되거나 분리되었을 경우 어떤 코드를 실행하고 싶다면 callback ref를 사용해야 함useRef()
훅을 사용하여 버튼 클릭 시 <input>
에 포커스를 하도록 하는 코드function TextInputWithFocusButton(props) {
const inputElem = useRef(null);
const onButtonClick = ( => {
// `current`는 마운트된 input element를 가리킴
inputElem.current. focus);
};
return (
<>
<input ref={inputElem} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
import React, { useState, useEffect } from "React";
function UserStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
};
});
if (isOnline === null) {
return '대기 중...';
}
return isOnline ? '온라인' : '오프라인';
}
useState()
, useEffect()
훅을 사용하는 부분이 동일import React, { useState, useEffect } from "React";
function UserListItem(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
};
});
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
use
로 시작하고 내부에서 다른 훅을 호출하는 하나의 자바스크립트 함수useUserStatus()
라는 커스텀 훅으로 추출해 낸 것이다.import React, { useState, useEffect } from "React";
function useUserStatus(userId) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
};
});
return isOnline;
}
useUserStatus()
훅으로 추출했기 때문에 다음과 같이 코드를 변경할 수 있다.function UserStatus(props) {
const isOnline = useUserStatus(props.user.id);
if (isOnline === null) {
return '대기 중...';
}
return isOnline ? '온라인' : '오프라인';
}
function UserListItem(props) {
const isOnline = useUserStatus(props.user.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.user.name}
</li>
);
}
userCounter()
커스텀 훅 만들기// src/chapter_07/userCounter.jsx
import React, { useState } from "react";
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
const increaseCount = () => setCount((count) => count + 1);
const decreaseCount = () => setCount((count) => Math.max(count - 1, 0));
return [count, increaseCount, decreaseCount];
}
export default useCounter;
useCounter()
을 사용하는 함수 컴포넌트를 만들어보자.useCounter()
훅을 사용하여 카운트를 관리하고, 최대 카운트 개수(MAX_CAPACITY)를 초과하면 경고 문구가 표시되도록 코드를 작성해보자.useEffect()
훅의 작동 방식을 확인하기 위해 두 개의 useEffect()
훅을 사용한다.useEffect()
훅은 컴포넌트가 마운트된 직후에 호출되며 이후 컴포넌트가 업데이트될 때마다 호출된다.useEffect()
훅은 컴포넌트가 마운트된 직후에 호출되며, count 값이 바뀔 때마다 호출되는데 용량 상태 isFull을 state에 저장한다.// src/chapter_07/Accommodate.jsx
import React, { useState, useEffect } from "react";
import useCounter from "./useCounter";
const MAX_CAPACITY = 10;
function Accommodate(props) {
const [isFull, setIsFull] = useState(false);
const [count, increaseCount, decreaseCount] = useCounter(0);
useEffect(() => {
console.log("======================");
console.log("useEffect() is called.");
console.log(`isFull: ${isFull}`);
});
useEffect(() => {
setIsFull(count >= MAX_CAPACITY);
console.log(`Current count value: ${count}`);
}, [count]);
return (
<div style={{ padding: 16 }}>
<p>{`총 ${count}명 수용했습니다.`}</p>
<button onClick={increaseCount} disabled={isFull}>
입장
</button>
<button onClick={decreaseCount}>퇴장</button>
{isFull && <p style={{ color: "red" }}>정원이 가득찼습니다.</p>}
</div>
);
}
export default Accommodate;
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Accommodate from './chapter_07/Accommodate';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Accommodate />
</React.StrictMode>
);
reportWebVitals();
useEffect()
훅이 호출된 것을 알 수 있다.useEffect()
훅이 호출되고 카운트 값은 1 증가 한다.useEffect()
훅이 호출된 이유는 컴포넌트가 업데이트 되었기 때문useEffect()
훅이 호출된 이유는 count 값이 변경되었기 때문실습 전체 코드
References
- 소플의 처음 만난 리액트
- https://www.inflearn.com/course/처음-만난-리액트/