오늘은 React의 몇 가지 hook에 관해 정리해보려고 한다.
1. `useState`
가장 기본적인 hook이며, 함수 컴포넌트에서 가변적인 상태를 갖게 해준다.
가장 기본적인 사용방법은 아래와 같다.
const [value, setValue] = useState(initialState);
// value를 nextState로 바꾸기
setValue(nextState);
그러면 초기에 `value`를 `initialState`로 설정하고 이후 `setValue`를 통해 `value`를 바꿔줄 수 있다. 하지만 이렇게 하면 setValue를 여러 번 수행한 경우, 마지막 setValue만 적용하게 된다. (렌더링 최적화를 위해 setState를 모아 한 번에 실행한다. batch update 방식)
함수형 업데이트
특정한 값을 넣는 것 보다 내부에 함수를 사용해 값을 변경하면 여러번 수행해도 마지막 결과가 아닌 함수를 여러 번 수행한 결과값이 저장된다.
const [value, setValue] = useState(0);
// 1번
setValue(value + 1);
setValue(value + 1);
setValue(value + 1);
// 1번 결과 : 1
// 2번
setValue((prev) => prev + 1);
setValue((prev) => prev + 1);
setValue((prev) => prev + 1);
// 2번 결과 : 3
console.log(value);
1번을 수행하면 value가 3이 될 것 같지만 1일 것이다. 생각한대로 3이 되도록 하려면 2번을 수행해야한다.
이때 마지막에 setValue(value + 1)을 하게 되면 2번도 무시되고 1로 변경된다.
자식 컴포넌트에서 부모 컴포넌트의 변수 변경하기
함수형 업데이트를 알기 전에는 자식 컴포넌트에 value와 setValue를 모두 props로 전해주었을 것이다. 이제 함수형 컴포넌트로 setValue만 전달해서 value를 변경할 수 있다.
// src > App.jsx
import { useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>부모 컴포넌트</h1>
<span>현재 카운트 : {count}</span>
<Child setCount={setCount}/>
</div>
)
}
export default App;
// src > components > Child.jsx
const Child = ({setCount}) => {
const handleAddCount = () => {
setCount(prev => prev + 1);
}
return <button onClick={() => {handleAddCount}}>Count 1 증가</button>
}
export default Child;
2. `useEffect`
React 컴포넌트가 렌더링 된 이후마다 특정 작업을 수행하도록 설정하는 hook.
기초적인 사용법은 아래와 같다.
// src/App.js
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
//처음 시작 직후 한번 출력됨
console.log("hello useEffect");
});
return <div>Home</div>;
}
export default App;
리렌더링은 state가 변경될 때마다 이루어진다. 즉 useState통해 state를 변경시킬 때 마다 "hello useEffect"가 출력 될 것이다. 즉. react + vite 기본 코드에 위 코드를 추가하면 count 버튼을 누를 때마다 "hello useEffect"가 출력된다.
의존성 배열
그런데 여러 state 중 하나만 변경되어도 실행된다면 너무 비효율적일 것이다. 이를 해결하기 위해 '의존성 배열(dependency array)'이 존재한다. 해당 배열에 있는 값이 바뀔 때만 실행된다.
function App() {
const [value, setValue] = useState('')
const [count, setCount] = useState(0)
useEffect(() => {
console.log('hello useEffect')
},[value])
return (
<>
<h1>useEffect</h1>
<input
type='text'
value={value}
onChange={(e) => {
setValue(e.target.value);
}}>
</input>
<button
onClick={() => {setCount(count+1)}}>{count}</button>
</>
)
}
위와 같은 코드를 실행해보면 `useEffect`가 `value`에만 의존하도록 설정되어있다. 즉 input에 값을 입력할 때마다 "hello useEffect"가 출력되지만 버튼을 눌렀을 땐 출력되지 않는다.
3. `useRef`
렌더링과 상관없이 특정 값을 저장할 수 있다.
기본적인 사용방법은 다음과 같다.
const ref = useRef(initialValue);
console.log(ref) // {current : initialValue}
값을 변경하려면 객체에 값을 변경하듯 변경하면 된다.
ref.current = nextValue;
console.log(ref) // {current : nextValue}
이렇게 보면 `useState`와의 차이점을 잘 모를 수 있다. `useState`는 `setState`를 할 때마다 리렌더링되지만, `useRef`는 리렌더링되지 않는다. 정리하면 다음과 같다.
`useState`는 값이 변경될 때 리렌더링이 필요한 경우 사용하고, `useRef`는 값이 변경되지만 리렌더링은 필요없는 경우 사용하면 된다.
이전 React 포스팅에서 다룬 프로젝트에서 input 태그에 `onChange`와 `setState`를 같이 사용하였다. 하지만 입력될 때마다 state가 변경되면 무의미한 리렌더링이 무수히 많이 일어날 것이다. 이럴 때 `useRef`를 사용하면 리렌더링이 일어나지 않고 값을 저장할 수 있다.
아래 코드를 실행해보면 'ref 증가' 버튼은 눌러도 변화가 없다가 'state 증가' 버튼을 누르면 변화가 생긴다. 즉 `setCount`에 의해 state 변화가 생겨 리렌더링이 발생해 변경된 값이 적용된 것이다.
function App() {
const [count, setCount] = useState(0);
const countRef = useRef(0);
const plusStateCountButtonHandler = () => {
setCount(count + 1)
}
const plusRefCountButtonHandler = () => {
countRef.current++;
// 누른다고 바로 화면에 변화가 보이진 않음
// 리렌더링되면(=Count 버튼을 누르면) 변화한 값이 보임
}
return (
<>
<h1>useRef & useState</h1>
<div>
state 영역. {count} <br></br>
<button onClick={plusStateCountButtonHandler}>state 증가</button>
</div>
<div>
Ref 영역. {countRef.current} <br></br>
<button onClick={plusRefCountButtonHandler}>ref 증가</button>
</div>
</>
)
}
useRef를 이용해서 DOM 요소에도 접근할 수 있다.
function App() {
const idRef = useRef('');
// 최초 렌더링 시에만 ref={idRef} 가 잇는 부분에 focus
useEffect(() => {
idRef.current.focus();
}, [])
return (
<>
<h1>useRef & useState</h1>
<div>
<div>
아이디 : <input type='text' ref={idRef}/>
</div>
<div>
비밀번호 : <input type='password'/>
</div>
</div>
</>
)
}
리렌더링 후 id input칸으로 focus를 할 수 있다.
다음에는 `useContext`와 `useMemo`에 대해서 정리할 계획이다.
'Frontend > Today I Learned' 카테고리의 다른 글
[JS] 이벤트 겹쳤을 때 막는 방법 (+ 이벤트 캡처링, 버블링) (0) | 2024.08.23 |
---|---|
[JS, redux] Redux 소개 및 기본 사용법 (0) | 2024.08.19 |
[CSS] 스크롤 바 스타일 (0) | 2024.08.13 |
[JS] Module 기초 정리 (0) | 2024.08.08 |
[git] 자주 사용하는 기초적인 git 명령어 정리 (0) | 2024.08.07 |