this 사용의 복잡성, 코드 재사용의 어려움 등을 해결하기 위해 도입되었다[1].상태를 선언하고 업데이트하는 데 사용된다.
state의 생성과 동시에 가져야할 초기값을 useState()에 인자로 넣어주면 state와 setState 두가지 요소를 배열 형태로 리턴 해준다.const [state,setState] = useState(초기값);
현재 상태 값은 state에 들어 있고 state를 변경 주고 싶을 때는 setState 함수를 이용해서 변경시켜 줄 수 있다.
setState()를 사용해서 state를 변경하면 해당 컴포넌트 화면에 다시 랜더링이 된다.
state: 컴포넌트가 가질 수 있는 상태를 말한다.
useState의 동작 원리
상태와 상태 업데이트 함수: useState는 배열을 반환하며, 첫 번째 요소는 현재 상태, 두 번째 요소는 상태를 업데이트하는 함수입니다. 예를 들어, const [value, setValue] = useState(initialState);와 같이 사용한다.
상태 업데이트 함수의 형태: setValue와 같은 상태 업데이트 함수는 두 가지 방식으로 호출할 수 있다.
직접 값으로 설정: setValue(newValue)처럼 새로운 값을 직접 전달.
콜백 함수를 사용하여 설정: setValue(prevValue => newValue)처럼 이전 값을 인자로 받아 새로운 값을 계산.
import { useState } from "react";
const UseState = () => {
const [value, setValue] = useState(0);
console.log(value);
return (
<div>
<p>
현재 카운터 값은 <b>{value}</b> 입니다.
</p>
<button onClick={() => setValue(value + 1)}>+1</button>
</div>
);
};
export default UseState;
import { useState } from "react";
const UseStateCallback = () => {
const [names, setNames] = useState([
"김세빈",
"김예빈",
"임승현",
"최보아",
"최민혁",
]);
const [input, setInput] = useState("");
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInput(e.target.value);
};
const handleUpload = () => {
setNames((prevState) => {
return [input, ...prevState];
});
};
return (
<div>
<input type="text" value={input} onChange={handleInputChange} />
<button onClick={handleUpload}>upload</button>
{names.map((name, idx) => {
return <p key={idx}>{name}</p>;
})}
</div>
);
};
export default UseStateCallback;
인자로 콜백함수만 받는 경우
컴포넌트가 랜더링이 될 때마다 실행
콜백은 컴포넌트가 화면에 랜더링된 직후에 불리는 것
콜백함수와 두번째 인자로 배열(dependency array)을 받는 경우
컴포넌트가 맨 처음 화면에 랜더링이 될때와 배열안의 값이 바뀔 때 실행
clean up
콜백함수의 리턴 값은 컴포넌트가 언마운트 될 때(화면에서 사라질 때) 실행이 된다.
예를 들어 구독 설정 및 해제 등이 있습니다.
import React, { useState, useEffect } from "react";
import Timer from "./Timer";
const UseEffectCleanUp = () => {
const [showTimer, setShowTimer] = useState(false);
return (
<div>
{showTimer && <Timer />}
<button onClick={() => setShowTimer(!showTimer)}>Toggle Timer</button>
</div>
);
};
export default UseEffectCleanUp;
import { useEffect } from "react";
export default function Timer() {
useEffect(() => {
const timer = setInterval(() => {
console.log("타이머 돌아가는 중...");
}, 1000);
return () => {
clearInterval(timer);
console.log("타이머가 종료됨");
};
}, []);
return (
<div>
<span>타이머 시작 콘솔 확인</span>
</div>
);
}
import React, { useState, useEffect } from "react";
const UseEffect = () => {
const [count, setCount] = useState(1);
const [name, setName] = useState("");
const handleCountUpdate = () => {
setCount(count + 1);
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};
useEffect(() => {
console.log("Effect 실행됨");
});
useEffect(() => {
console.log("count 변화");
}, [count]);
useEffect(() => {
console.log("name 변화");
}, [name]);
useEffect(() => {
console.log("mounting");
}, []);
return (
<div>
<button onClick={handleCountUpdate}>증가</button>
<span>count: {count}</span>
<input type="text" value="" onChange={handleInputChange} />
<span>name: {name}</span>
</div>
);
};
export default UseEffect;
const ref =useRef(value) -> {current:value}import React, { useState, useRef } from "react";
export default function UseRef() {
const [count, setCount] = useState(0);
const countRef = useRef(0);
let countVal = 0;
console.log(countRef);
console.log("랜더링");
const increaseCountState = () => {
setCount(count + 1);
};
const increaseRef = () => {
countRef.current++;
console.log("ref:", countRef.current);
};
const increaseVal = () => {
countVal++;
console.log("val:", countVal);
};
return (
<div>
<p>val: {countVal}</p>
<p>state: {count}</p>
<p>ref: {countRef.current}</p>
<button onClick={increaseCountState}>state++</button>
<button onClick={increaseRef}>ref++</button>
<button onClick={increaseVal}>val++</button>
</div>
);
}
import { useEffect, useRef } from "react";
export default function UseRefDOM() {
const inputRef = useRef<HTMLInputElement>(null); // HTMLInputElement 타입 명시
useEffect(() => {
console.log();
if (inputRef.current) {
// current가 null이 아닌 경우에만 focus 호출
inputRef.current.focus();
}
}, []);
return (
<div>
<input ref={inputRef} type="text" placeholder="id" />
<button>로그인</button>
</div>
);
}
import { useRef } from "react";
export function useThrottle(callback: () => void, delay: number) {
const lastRun = useRef(Date.now());
return () => {
const timeElapsed = Date.now() - lastRun.current;
if (timeElapsed >= delay) {
callback();
lastRun.current = Date.now();
}
};
}
// 사용 예시
import { useRef } from "react";
export function useThrottle(callback: () => void, delay: number) {
const lastRun = useRef(Date.now());
return () => {
const timeElapsed = Date.now() - lastRun.current;
if (timeElapsed >= delay) {
callback();
lastRun.current = Date.now();
}
};
}
React Hooks는 함수형 컴포넌트를 더욱 강력하게 만들어주는 도구로, 현대적인 React 개발에 필수적인 요소이다.
Citations:
[1] https://www.youtube.com/watch?v=qjEcsNYFWYg
[2] https://velog.io/@velopert/react-hooks
[3] https://www.w3schools.com/react/react_hooks.asp
[4] https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/
[5] https://deview.kr/data/deview/session/attach/Recoil_%E1%84%8B%E1%85%AA%E1%86%BC%E1%84%8B%E1%85%B1%E1%84%85%E1%85%B3%E1%86%AF_%E1%84%80%E1%85%A8%E1%84%89%E1%85%B3%E1%86%BC%E1%84%8C%E1%85%AE%E1%86%BC.pdf
[6] https://www.geeksforgeeks.org/reactjs-hooks/
[7] https://www.freecodecamp.org/news/react-hooks-fundamentals/
[8] https://corner-ds.tistory.com/228
[9][useState](https://www.youtube.com/watch?v=G3qglTF-fFI&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=1&t=645s)
[10][useEffect](https://www.youtube.com/watch?v=kyodvzc5GHU&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=2)
[11][custom hook](https://www.youtube.com/watch?v=S6POUU2-tr8&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=10)