[React] useState
리액트 훅 중에 기본 중에 기본인 useState에 대해 알아보자.
React에서 State란?
컴포넌트가 가질 수 있는 상태를 의미한다.
만약 시계라는 컴포넌트가 있으면 State는 time을 가질 수 있다.
useState는 컴포넌트의 상태를 가져오고 생성하고 업데이트를 해줄 수 있는 도구를 제공해 준다.
useState의 기본형태
const [state, setState] = useState(초기값);
- state : 현재의 상태값
- setState : state를 변경시켜주고 싶을 때 사용
useState활용
const [time, setTime] = useState(5);
- 초기값으로 5를 가지게 되고 이때는 time = 5이다.
- 시간을 변경하려면 setTime(6)으로 넣어주면 time이 6이 된다.
(인자에다가 변경하고 싶은 값을 넣어주면 된다.)
setState함수를 사용해서 state를 변경(업데이트)해주면 해당 컴포넌트는 렌더링이 된다.
time state가 변경될 때마다 화면이 업데이트가 된다.
예제 1 (시계)
import { useState } from "react";
function App() {
return (
<>
<UseState />
</>
);
}
const UseState = () => {
const [time, setTime] = useState(1);
const handleClick = () => {
setTime(time + 1);
};
return (
<div>
<span>현재 시각: {time}시</span>
{/* 클릭할때마다 1씩 증가 */}
<button onClick={handleClick}>Update</button>
</div>
);
};
export default App;
업데이트를 클릭할 때마다 화면이 렌더링이 된다.
진짜로 업데이트를 클릭할때마다 화면이 렌더링이 되는지 확인하기 위해 콘솔 추가!
import { useState } from "react";
function App() {
return (
<>
<UseState />
</>
);
}
const UseState = () => {
const [time, setTime] = useState(1);
const handleClick = () => {
setTime(time + 1);
};
console.log("업데이트!!");
return (
<div>
<span>현재 시각: {time}시</span>
{/* 클릭할때마다 1씩 증가 */}
<button onClick={handleClick}>Update</button>
</div>
);
};
export default App;
진짜 시계로 만들기 (현재 시각이 12시일 때 업데이트를 클릭하면 1로 되돌아감)
import { useState } from "react";
function App() {
return (
<>
<UseState />
</>
);
}
const UseState = () => {
const [time, setTime] = useState(1);
const handleClick = () => {
let newTime;
if (time >= 12) {
newTime = 1;
} else {
newTime = time + 1;
}
setTime(newTime);
};
console.log("업데이트!!");
return (
<div>
<span>현재 시각: {time}시</span>
{/* 클릭할때마다 1씩 증가 */}
<button onClick={handleClick}>Update</button>
</div>
);
};
export default App;
예제 2 (입력 리스트)
import { useState } from "react";
function App() {
return (
<>
<UseState2 />
</>
);
}
const UseState2 = () => {
const [names, setNames] = useState(["홍길동", "김민수"]);
const [input, setInput] = useState(""); // 현재 input안에 무슨 값을 가지고 있는지 트래킹 해주는 state
const handleInputChange = ({ target }) => {
setInput(target.value);
};
console.log(input);
return (
<div>
<input type="text" onChange={handleInputChange} />
<button>Update</button>
{names.map((name, key) => (
<p key={key}>{name}</p>
))}
</div>
);
};
export default App;
사용자가 input을 입력할 때마다 입력값이 나옴
이제 input에 이영희를 입력한 뒤 업로드버튼을 클릭하면 names에 저장이 되게 만들어 보자
입력값을 names에 저장되게 만들 때 아래와 같이 작성하면 안 된다.
setNames(["홍길동", "김민수", "이영희"]); // 이렇게 하면 안된다.
업데이트될 값은 이전값 + 입력값이다.
이전에 존재하는 state와 밀접한 관계가 있음으로 인자에다가 콜백함수를 전달해 주어야 된다.
콜백의 인자로는 업데이트해주기 전 이전상태의 state를 가지고 있다.
// 올바른 작성법
setNames((prevState) => [input, ...prevState]);
전체 코드
import { useState } from "react";
function App() {
return (
<>
<UseState2 />
</>
);
}
const UseState2 = () => {
const [names, setNames] = useState(["홍길동", "김민수"]);
const [input, setInput] = useState(""); // 현재 input안에 무슨 값을 가지고 있는지 트래킹 해주는 state
const handleInputChange = ({ target }) => {
setInput(target.value);
};
const handleUpload = () => {
// setNames(["홍길동", "김민수", "이영희"]); // 이렇게 하면 안된다.
// 새로 업데이트 되는 값은 이전값 + 입력값 이다.
// 콜백함수를 전달해주어야 된다.
setNames((prevState) => [input, ...prevState]);
};
console.log(input);
return (
<div>
<input type="text" onChange={handleInputChange} />
<button onClick={handleUpload}>Update</button>
{names.map((name, key) => (
<p key={key}>{name}</p>
))}
</div>
);
};
export default App;
preveState에 이전 state값이 들어가 있는지 확인해 보자면
const handleUpload = () => {
setNames((prevState) => {
console.log("이전값: ", prevState);
return [input, ...prevState];
});
};
(잊지 말자!!) useState는 state가 업데이트될 때마다 화면이 렌더링이 된다.
만약 useState초기값을 가져올 때 무거운 작업을 해야 될 경우 (엄청 오래 걸리는 계산)
const [state, setState] = useState(무거운 작업 하는 중...)
컴포넌트가 계속해서 렌더링이 되기 때문에 무거운 작업을 하는 함수가 계속 다시 호출되는 현상이 반복된다.
이렇게 될 경우 성능에 굉장히 안 좋아진다.
리프레쉬하는 순간부터 엄청 무거운 작업이 실행되고,
state를 업데이트해줄 때마다 엄청 무거운 작업이 계속 실행되고 있다.
(엄청 비효율적이다.)
heavyWork함수가 맨 처음 렌더링 될 때만 불려지게 하고 싶다.
초기값을 넣어줄 인자에 콜백을 넣어주면 된다.
import { useState } from "react";
function App() {
return (
<>
<UseState2 />
</>
);
}
const UseState2 = () => {
const heavyWork = () => {
console.log("엄청 무거운 작업!!!");
return ["홍길동", "김민수"];
};
const [names, setNames] = useState(() => heavyWork());
const [input, setInput] = useState("");
const handleInputChange = ({ target }) => {
setInput(target.value);
};
const handleUpload = () => {
setNames((prevState) => {
console.log("이전값: ", prevState);
return [input, ...prevState];
});
};
return (
<div>
<input type="text" onChange={handleInputChange} />
<button onClick={handleUpload}>Update</button>
{names.map((name, key) => (
<p key={key}>{name}</p>
))}
</div>
);
};
export default App;
초기값을 가져올 때 무거운 작업을 해야 된다면 값을 넣어주는 게 아니라 콜백함수를 넣어줘야 된다.
그러면 화면이 처음 렌더링 될 때만 함수(무거운 작업)를 가져오게 된다.
정리
1.
const [state, setState] = useState(초기값);
useState는 state, setState를 배열형태로 리턴해 준다.
state는 현재 상태값이 들어있고, setState는 state를 변경해 준다.
setState를 사용해서 state를 변경해 줄 때마다 화면이 렌더링이 된다.
2.
setState((prevState)=>{
// some works...
return newState;
});
새로 변경될 state값이 이전 State값과 연관되어 있으면
setState 인자로 새로운 state리턴하는 콜백함수를 넣어주는 것이 좋다.
3.
useState(()=>{
return heavyWorks();
});
useState를 사용해서 초기값을 받아올 때 어떤 무거운 일을 해야 된다면
useState에 인자로 콜백함수를 넣어준다면 맨 처음 렌더링 될 때만 실행되게 할 수 있다.
※ 이 글은 별코딩 리액트 훅 강의를 보고 정리한 글입니다.