useState
useState란?
- state의 초기값 설정
- getter/setter 추가
useState의 특징
- 사용자가 직접 업데이트할 수 있는 state 변수를 선언하고 관리할 수 있다.
- 초기값을 설정할 수 있다.
- const 키워드로 선언을 한다.
- 잘못된 수정을 방지하기 위한 처리다.
- 만약 const가 아니라 var나 let으로 선언하면 값이 바뀌긴 하지만 리렌더링이 일어나지는 않는다.
- getter와 setter로 state를 관리한다.
- 첫번째로 명시하는 인자는 getter가 된다.
- 두번째로 명시하는 인자는 setter가 된다.
- getter는 순수하게 변수처럼 사용한다.
- setter의 콜백에는 기존 값과 변경될 값을 나타내게 된다.
setCount((val) => val + 1)
처럼 사용한다.val + 1
이나++ val
은 먹히지만val ++
은 동작하지 않는다.
- 비동기로 동작한다.
사용 방법
- useState를 import 하기
import { useState } from "react";
- 초기값/getter/setter 설정하기
- 작성 방법
const [변수명, set변수명] = useState(초기값);
- 예시
const [count, setCount] = useState(0);
- 작성 방법
- 기존의 import React from “react”;에서 import React, {useState} from “react”;로 변경한다.
- const [val, setVal] = useState(“원하는 값”);을 선언한다.
- 만약 함수를 선언해서 그 안에서 값을 변경한다면 setVal(val + “XXX”);처럼 하면 된다.
- 특징
- useState는 배열을 반환한다.
- val와 setVal은 그 어떤 이름을 써도 상관이 없긴 하지만
본인의 편의나 팀간의 협력을 위해서라도 val와 setVal과 같이 쓰는 것이 좋다. - 만약 setVal은 사용하지 않고 val만 사용하고 싶다면 const [val] = useState(“원하는 값”)[0];처럼 사용하면 된다.
- useState를 사용하게 된다면 this를 사용하지 않아도 된다.
- 리액트의 useState를 통한 setXXX를 사용할 때 값이 아닌 함수로 전달할 수 있는데,
이 때 사용되는 변수는 이전에 setXXX를 통해서 설정된 현재의 XXX의 값을 가져올 수 있다.
- 특징
예시
import { useState } from "react";
const Sample = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount((val) => val - 1)}>- 1</button>
<button onClick={() => setCount((val) => val + 1)}>+ 1</button>
</>
);
};
export default Sample;
useState는 비동기적으로 동작한다.
- useState는 성능 최적화를 위해서 비동기적을 동작한다.
- 리액트는 성능을 최적화하기 위해 setState를 배치 처리한다.
- setState 연속으로 호출하면 그 동작들을 한 번에 모아서 한꺼번에 랜더링한다.
- 배치 (batch)
- 리액트가 성능 향상을 위해 여러 개의 setState를 하나의 리렌더링으로 묶는 과정
- 16ms동안 변경된 State들을 하나로 묶는다.
- 16ms동안 동일한 setState를 아무리 실행해봤자 1번만 실행된다.
- 동일한 State에 대해서 여러 번 setState를 실행했다면 마지막으로 실행한 setState만 적용된다.
- 리액트는 성능을 최적화하기 위해 setState를 배치 처리한다.
- 사실 useState의 setter는 콜백을 안 써도 동작한다.
- 예시 :
setCount(count + 1)
- 예시 :
- useState를 동기적으로 처리하려면 인자로 콜백함수를 집어넣거나 useEffect의 의존성 배열을 활용하면 된다.
- 테스트 코드
import { useState } from "react";
const Sample = () => {
const [count, setCount] = useState(0);
const fun1 = () => {
//1씩 증가한다.
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
const fun2 = () => {
//3씩 증가한다.
setCount((val) => val + 1);
setCount((val) => val + 1);
setCount((val) => val + 1);
};
return (
<>
<h1>3씩 증가시켜보자</h1>
<h1>{count}</h1>
<button onClick={() => fun1()}>비동기적으로 동작</button>
<button onClick={() => fun2()}>동기적으로 동작</button>
</>
);
};
export default Sample;
useInput (useState 활용)
- useState를 활용하여 유효성 검사를 할 때 많이 사용한다.
초기값 설정하기
- 단순하게 초기값을 설정한다.
import { useState } from "react";
const useInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
return { value };
}
const Sample = () => {
const birthYear = useInput(2024);
return (
<>
<h1>useInput 연습하기</h1>
<input placeholder="몇 년도에 태어나셨나요?" value={birthYear.value} />
</>
);
};
export default Sample;
초기값 설정 시의 성능 변경점
- useState의 초기값을 함수로 설정해야 하는 경우가 있다.
- 데이터베이스에서 데이터를 조회하는 경우
- 처리해야하는 데이터의 양이 많은 경우
- 초기값을 함수로 주게 되면 렌더링이 될 때마다 useState가 다시 호출되기 때문에 초기값을 반환하는 함수도 늘 새로 실행된다.
- 이를 막기 위해서는 초기값을 반환하는 함수를 콜백으로 설정하면 된다.
- 좋은 예시
const [value, setValue] = useState(() => 함수명([파라미터]));
- 나쁜 예시
const [value, setValue] = useState(함수명([파라미터]));
- 좋은 예시
값 변경하기
- 값을 변경하는 기능을 추가한다.
import { useState } from "react";
const useInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleInputValue = (event) => {
setValue(event.target.value);
}
return { value, handleInputValue};
}
const Sample = () => {
const birthYear = useInput(2024);
return (
<>
<h1>useInput 연습하기</h1>
<input placeholder="몇 년도에 태어나셨나요?" value={birthYear.value} onChange={birthYear.handleInputValue}/>
</>
);
};
export default Sample;
유효성 검사 기능 추가하기
- 유효성 검사를 통과했을 때만 값을 변경할 수 있는 기능을 추가한다.
import { useState } from "react";
//숫자인지 확인
const isNumber = (value) => {
return !isNaN(value)
};
const useInput = (initialValue, validator) => {
const [value, setValue] = useState(() => initialValue);
const handleInputValue = (event) => {
const newValue = event.target.value;
let isValid = false; //유효성 검증 통과 여부
if (typeof validator === 'function') {
isValid = validator(newValue);
}
if(isValid){
setValue(newValue);
} else {
//후속 조치
console.log("유효성 검사 통과 안 됨");
}
}
return { value, handleInputValue};
}
const Sample = () => {
const birthYear = useInput(2024, isNumber);
return (
<>
<h1>useInput 연습하기</h1>
<input placeholder="몇 년도에 태어나셨나요?" value={birthYear.value} onChange={birthYear.handleInputValue} />
</>
);
};
export default Sample;
useTabs (useState 활용)
- 값의 전환을 쉽게 사용하기 위한 기능
- 화면 전환 시에 유용하다.
import { useState } from "react";
const items = [
{
tabName: "첫번째 버튼",
content: "첫번째 내용"
},
{
tabName: "두번째 버튼",
content: "두번째 내용"
},
{
tabName: "세번째 버튼",
content: "세번째 내용"
}
];
const useTabs = (initialTab, allTabs) => {
if(!allTabs || !Array.isArray(allTabs)){
return;
}
const [currentIndex, setCurrentIndex] = useState(() => initialTab);
return {
currentItem : allTabs[currentIndex],
changeItem : setCurrentIndex
};
};
const Sample = () => {
const {currentItem, changeItem} = useTabs(0, items);
return (
<>
<h1>useTabs 연습하기</h1>
{items.map((item, index) => <button key={index} onClick={() => changeItem(index)}>{item.tabName}</button>)}
<div>{currentItem.content}</div>
</>
);
};
export default Sample;
객체를 변경하려면 어떻게 해야할까?
객체를 변경하려면 … 연산자를 활용하면 된다.
import { useState } from "react";
const [ userInfo, setUserInfo ] = userState({
name: '',
mobile: '',
email: '',
});
setUserInfo({...userInfo, email: '이메일주소'})
실제 input과 연결해보자.
input의 값을 변경했을 때 자동으로 연동되도록
e.target.name
과 e.target.value
를 활용하자.
import { useState } from "react";
const [input, setInput] = useState({
loginId: "",
password: ""
});
/**
* ID/PW 값 변경
* @param e Form 이벤트
*/
function onChange(e){
setInput({
...input,
[e.target.name]: e.target.value
});
};
<input type="text" name="loginId" value={input.loginId} onChange={onChange}/>