리액트에서 state 객체는 컴포넌트의 동적인 데이터를 관리하는 데 필수적인 역할을 한다. state 객체를 활용하여 UI의 변화를 관리하고, 사용자와의 상호작용에 따라 상태를 업데이트할 수 있다. 이번 포스팅에서는 state 객체의 기본 개념, 사용 방법, 그리고 몇 가지 유용한 팁을 정리해 보겠다.
https://react.dev/learn/updating-arrays-in-state#removing-from-an-array
리액트에서 배열을 상태로 사용할 때는 배열을 직접 변경하는 함수를 피하고, 새로운 배열을 반환하는 함수를 사용하는 것이 중요하다. 아래는 배열 조작 시 피해야 할 함수와 지향해야 할 함수의 목록이다.
| 구분 | 피해야 할 함수 (Mutates the array) | 지향해야 할 함수 (Returns a new array) |
|---|---|---|
| 추가 | push, unshift | concat, [...arr] (spread syntax) |
| 제거 | pop, shift, splice | filter, slice |
| 교체 | splice, arr[i] = ... (assignment) | map |
| 정렬 | reverse, sort | 배열을 복사한 후 정렬 (예: const sortedArr = [...arr].sort(...)) |
추가:
push와 unshift는 기존 배열을 직접 수정하므로, 리액트의 상태 관리 원칙에 어긋난다.concat과 스프레드 문법([...arr])은 기존 배열을 변경하지 않고 새로운 배열을 생성한다.제거:
pop, shift, splice는 배열을 직접 수정하므로, 상태 관리에 문제가 생길 수 있다.filter와 slice는 조건에 따라 새로운 배열을 반환하여 상태 업데이트에 적합하다.교체:
splice와 배열 인덱스를 사용한 대입은 기존 배열을 변경하므로 피해야 한다.map을 사용하면 각 요소를 변형하여 새로운 배열을 생성할 수 있다.정렬:
reverse와 sort는 기존 배열을 직접 수정하므로 사용을 피해야 한다.const sortedArr = [...arr].sort(...)와 같이 작성할 수 있다.리액트에서 배열을 사용할 때는 항상 배열이 직접 수정되지 않도록 주의해야 한다. 위의 목록을 참고하여, 상태 관리의 원칙에 맞는 방법으로 배열을 조작하도록 하자.
리액트에서 상태를 관리하기 위해 useState 훅을 사용한다. 기본적인 사용 방법은 다음과 같다.
import { useState } from "react";
import './App.css'
function App(){
const [count, setCount] = useState(0);
const increase=()=>{
setCount(cnt => cnt+1)
}
const decrease=()=>{
setCount(cnt => cnt-1)
}
const reset=()=>{
setCount(0)
}
return(
<div>
<p>count:{count}</p>
<button onClick={increase}>증가</button>
<button onClick={decrease}>감소</button>
<button onClick={reset}>리셋</button>
</div>
)
}
export default App

import { useState } from "react";
import './App.css'
function App() {
const [obj1, setObj1] = useState({ name: "최혜린" });
function changeName() {
obj1.name = "최영준";
setObj1(obj1);
}
return (
<div>
<h1>{obj1.name}</h1>
<button onClick={() => changeName()}>이름 바꾸기</button>
</div>
);
}
export default App;

import { useState } from "react";
import './App.css'
function App() {
const [obj1, setObj1] = useState({ name: "최혜린", age: 30 });
function changeName() {
setObj1({
...obj1,
name: "최영준"
});
}
return (
<div>
<h1>{obj1.name}</h1>
<h3>{obj1.age}</h3>
<button onClick={() => changeName()}>이름 바꾸기</button>
</div>
);
}
export default App;

import { useState } from "react";
import './App.css'
function App() {
const [obj1, setObj1] = useState({ name: "최혜린", score: 0 });
const [obj2, setObj2] = useState({ name: "최영준", score: 0 });
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
function score1() {
setObj1({
...obj1,
score: obj1.score + 10
});
}
function score2() {
setObj2({
...obj2,
score: obj2.score + 10
});
}
function reset() {
setObj1({
...obj1,
score: 0
});
setObj2({
...obj2,
score: 0
});
}
function inputScore() {
if (input1 === "최혜린") {
setObj1({
...obj1,
score: obj1.score + parseInt(input2)
});
} else if (input1 === "최영준") {
setObj2({
...obj2,
score: obj2.score + parseInt(input2)
});
} else {
alert("최혜린, 최영준 중 하나를 입력하세요");
}
}
const style = {
display: "flex"
};
return (
<>
<input type="text" value={input1} onChange={e => setInput1(e.target.value)} placeholder="최혜린 or 최영준" />
<input type="number" value={input2} onChange={e => setInput2(e.target.value)} placeholder="점수를 입력하세요" />
<button onClick={() => inputScore()}>점수 추가</button>
<div style={style}>
<div>
<h1>{obj1.name}</h1>
<h3>{obj1.score}</h3>
<button onClick={() => score1()}>점수 올리기</button>
</div>
<div>
<h1>{obj2.name}</h1>
<h3>{obj2.score}</h3>
<button onClick={() => score2()}>점수 올리기</button>
</div>
<button onClick={() => reset()}>리셋</button>
</div>
</>
);
}
export default App;

import { useState } from "react";
import './App.css'
function App() {
const [arr1, setArr1] = useState(["한식", "중식", "양식"]);
function loop(st) {
setArr1([
...arr1,
st
]);
}
return (
<div>
<ul>
{arr1.map((str, i) => (
<li key={i}>{str}</li>
))}
</ul>
<button onClick={() => loop("일식")}>클릭</button>
</div>
);
}
export default App;
import { useState } from "react";
import './App.css'
function App(){
const [arr1,setArr1] = useState(["한식","중식","양식"])
function insertStr(st){
setArr1(arr1.concat(st))
}
return(
<div>
<ul>
{arr1.map((str,i)=>(
<li key={i}>{str}</li>
))}
</ul>
<button onClick={()=>insertStr("일식")}>클릭</button>
</div>
)
}
export default App

import { useState } from "react";
import './App.css'
function App(){
const [input1,setInput1]=useState("")
const [arr1,setArr1]=useState([])
function addList(input1){
setArr1(arr1.concat(input1))
setInput1("")
}
return(
<div>
<ul>
{arr1.map((str,i)=>(
<li key={i}>{str}</li>
))}
</ul>
<input type="text" value={input1} onChange={e=>setInput1(e.target.value)} />
<button onClick={()=>addList(input1)}>추가</button>
</div>
)
}
export default App

import { useState } from 'react';
import './App.css'
function App() {
const [arr1, setArr1] = useState([
{ id: 1, menu: "불고기피자" },
{ id: 2, menu: "고구마피자" },
{ id: 3, menu: "포테이토피자" },
{ id: 4, menu: "불닭피자" },
{ id: 5, menu: "치즈피자" }
]);
function deleteMenu(num) {
setArr1(arr1.filter((obj, i) => i !== num));
}
return (
<div>
<ul>
{arr1.map((obj, i) => (
<li key={i}>{obj.menu}</li>
))}
</ul>
<button onClick={() => deleteMenu(2)}>포테이토피자 삭제</button>
</div>
);
}
export default App;

import { useState } from 'react';
import './App.css'
function App() {
const [arr1, setArr1] = useState([
{ id: 1, menu: "불고기피자" },
{ id: 2, menu: "고구마피자" },
{ id: 3, menu: "포테이토피자" },
{ id: 4, menu: "불닭피자" },
{ id: 5, menu: "치즈피자" }
]);
function deleteMenu(num) {
setArr1(arr1.filter((obj) => obj.id !== num));
}
return (
<div>
<ul>
{arr1.map((obj) => (
<li key={obj.id}>{obj.menu}</li>
))}
</ul>
<button onClick={() => deleteMenu(2)}>포테이토피자 삭제</button>
</div>
);
}
export default App;

import { useState } from 'react'
import './App.css'
function App(){
const [inputs,setInput]=useState({
name: '',
nickname :''
});
function onchange(e){
const{value,name} = e.target
setInput({
...inputs,
[name]:value
})
}
return(
<div>
<input type="text" name='name' value={inputs.name} onChange={onchange} />
<input type="text" name='nickname' value={inputs.nickname} onChange={onchange} />
<div>
<p>값:</p>
{inputs.name} | {inputs.nickname}
</div>
</div>
)
}
export default App
구조분해할당을 통해 코드의 가독성을 높일 수 있다. const {name,nickname} = inputs
<input type="text" name='name' value={name} onChange={onchange} />
inputs.name 대신 name을 사용할 수 있어 가독성이 향상된다.리액트에서 객체 또는 배열을 상태로 사용할 때는 복잡한 데이터 구조를 쉽게 다룰 수 있도록 몇 가지 팁을 사용할 수 있다.
객체 상태를 사용할 때는 구조 분해 할당을 활용해 불변성을 유지해야 한다.
const [user, setUser] = useState({ name: "홍길동", age: 25 });
const updateName = (newName) => {
setUser({ ...user, name: newName });
};
배열 상태를 관리할 때는 배열 메서드를 사용하여 새로운 배열을 반환해야 한다.
const [items, setItems] = useState([]);
const addItem = (item) => {
setItems([...items, item]);
};
const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};