State란 컴포넌트 내부에서 바뀔 수 있는 값을 의미합니다.
(UI(엘리먼트)로의 반영을 위해서)
const name = "훈이"
이 값이 바뀌어야하는 정보라면 state로 생성
State를 만들 때는
useState()를 사용 (훅)
import React, { useState } from 'react';
function GrandFather() {
const [name, setName] = useState("신형만"); // state! (여기서 "신형만"은 초기값)
return <Mother grandFatherName={name} />;
}
// .. 중략
const [ value, setValue ] = useState( 초기값 );
배열의 구조분해 할당 사용
const [name, setName] = useState("신형만");
state의 이름,set 을 붙이고 state의 이름을 붙임 useState( ) 의 인자에는 이 state의 원하는 초기값(initial state)을 넣어줌// src/App.js
import React, { useState } from "react";
function Child(props) {
return <div>{props.grandFatherName}</div>;
}
function Mother(props) {
return <Child grandFatherName={props.grandFatherName} />;
}
function GrandFather() {
const [name, setName] = useState("신형만"); // state를 생성
return <Mother grandFatherName={name} />;
}
function App() {
return <GrandFather />;
}
export default App;
state를 변경할때는
setValue(바꾸고 싶은 값)

할아버지 이름 바꾸기 라는 라벨을 가진 버튼 만들기setName 을 실행 할 수 있도록 준비GrandFather에 있는 setName을 Child에게 전달을 해줘야 함. 주고 받고, 주고 받고해서 전달import React, { useState } from "react";
function App() {
const [name, setName] = useState("훈이");
function onClickHandler() {
setName("울보자식");
}
return (
<div>
{name}
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
onClickHandler 라는 함수를 만들고 onClick={} 에 넣어줌이벤트 핸들러훈이가 울보자식으로 바뀐다.input에서는 보통 사용자가 입력한 값을 state로 관리하는 패턴을 많이 사용
import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
return (
<div>
<input type="text" />
</div>
);
};
export default App;
value라는 state를 생성import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
const inputValue = event.target.value;
setValue(inputValue);
};
console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?
return (
<div>
<input type="text" onChange={onChangeHandler} value={value} />
</div>
);
};
export default App;
input에 onChange라는 이벤트를 불러내고, 생성한 이벤트 핸들러 함수를 넣어줌
import React, { useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
const inputValue = event.target.value;
setValue(inputValue);
};
console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?
return (
<div>
<input type="text" onChange={onChangeHandler} value={value} />
</div>
);
};
export default App;
이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용 event.target.value 로 꺼내 사용 value를 input의 attribute인 value에 넣어주면 input과 state 연결하기 끝!💡 꼭 onChange와 value를 둘 다 연결해야만 하나?(제어 컴포넌트의 개념)
React에서는 input 요소의 value 속성과 onChange 이벤트를 함께 사용하여 입력 필드의 상태를 관리하는 것이 일반적.
이러한 패턴을 제어 컴포넌트(Controlled Component)라고 한다.
( React에 의해 값이 제어되는 컴포넌트가 제어 컴포넌트
= state에 의해 값이 제어되는 input 컴포넌트는 제어 컴포넌트 )
제어 컴포넌트에서는 React의 상태가 소스 오브 트루스(source of truth) 역할을 하며, 이를 통해 입력 필드의 현재 값이 항상 React 컴포넌트의 상태와 동기화된다.
source of truth?어떤 정보의 정확하고 최신의 상태를 유지하는 주된 위치 또는 저장소.
위의 예시에서 리액트의 state이 input의 value와 엮여있으므로 state 자체가 input의 변경된 정보 자체를 가리킴
데이터의 일관성, 정확성을 유지하기 위해 필수!
💡 비제어 컴포넌트로 관리하면 안되나? (value를 연결하기 싫다)
useState를 사용하면서 input 요소의 value 속성에 상태를 연결하지 않는 경우, 이는 비제어 컴포넌트와 유사한 동작을 하게 된다.
이렇게 구현하면 input 요소는 자체적으로 내부 상태를 유지하게 되고, React의 상태는 입력값과 동기화되지 않는다.
다만, onChange 이벤트를 통해 입력값의 변경을 감지하고 React의 상태를 업데이트할 수는 있다.
이렇게 하면 사용자 입력에 대해 반응할 수 있지만, 입력 필드의 값은 React 상태에 의해 제어되지 않는다.
이 방식은 입력 필드가 React의 상태를 기반으로 렌더링되지 않는다.
불변성: 메모리에 있는 값을 변경할 수 없는 것
자바스크립트의 데이터 형태중에 원시 데이터는 불변성 O
원시 데이터가 아닌 객체, 배열, 함수는 불변성 X
let number = 1 선언을 하면 메모리에는 1 이라는 값이 저장 => number 라는 변수는 메모리에 있는 1을 참조
let secondNumber = 1 다른 변수를 선언을 했다고 가정 => 이때도 이미 메모리에 생성되어 있는 1이라는 값을 참조
즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라봄
number === secondNumber 값이 true 가 된다.
let 객체1 = {name: ‘kim’} 이라는 값을 선언하면 메모리에 객체1이 저장
let 객체2 = {name: ‘kim’} 이라고 같은 값을 선언하면 객체2 메모리 공간에 새롭게 저장
결국 객체1 === 객체2 는 false 가 된다.
기존 1이던 number를 number = 2 로 새로 값을 할당하면, 메모리에서는?
원시 데이터는 불변성이 있기에 기존 메모리에 저장 되어있는 1 의 값은 변하지 않음
=> 새로운 메모리 저장공간에 2가 생기고 number는 새로운 메모리 공간에 저장된 2를 참조
secondNumber 를 콘솔에 찍으면 여전히 ' 1 ' 이라고 뜬다.
=> number와secondNumber는 각각 다른 메모리 저장공간을 참조 하기 때문*
객체1.name = ‘park’ 로 새로운 값을 할당한다면?
객체는 불변성이 없기에 기존 메모리 저장공간에 있는 {name: ‘kim’} 이라는 값이 {name : ‘park’} 으로 바뀔뿐
정리
원시데이터 : 수정 시 메모리에 저장된 값 자체가 바뀌지 않음, 새로운 메모리 저장공간에 새로운 값 저장
그 외 데이터 : 기존 저장되어있던 메모리 저장공간의 값 자체를 바꿈
💡 리액트에서는 화면을 리렌더링 할지 말지 결정할 때 state의 변화를 확인한다.
=> state 변화 유무를 확인하는 방법 : state의 변화 전, 후의 메모리 주소를 비교💥
- state가 변했다? 리렌더링 해 ~
- state가 안 변했다? 리렌더링 하지마 ~
리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 하면 값은 바뀌지만 메모리주소는 변함이 없음
=> 💡 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못함 => 리렌더링이 제대로 수행 되지 않음
배열을
setState할 때 불변성을 지켜주기 위해 직접 수정을 가하지 않고
전개 연산자(...)를 사용해서 기존의 값을 복사 후 값을 수정(추가)하는 식으로 구현
(배열.push 등은 불변성을 깨뜨리니까 사용x)
import React, { useState } from "react";
function App() {
const [dogs, setDogs] = useState(["말티즈"]);
function onClickHandler() {
// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다.
// 그리고 나서 항목을 추가합니다.
setDogs([...dogs, "시고르자브르종"]);
}
console.log(dogs);
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
😩 아래 코드는 불변성을 위반 하는 코드이다. 불변성을 유지하는 코드로 변경해보시오
import React, { useState } from 'react';
function BadCounter() {
const [items, setItems] = useState([1, 2, 3]);
const addItem = () => {
items.push(items.length + 1); // 배열에 직접 push, 불변성 위반
setItems(items); // 상태 업데이트
};
return (
<div>
<button onClick={addItem}>Add Item</button>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
😎 변경 후
=> 새 배열 생성 -> 기존 항목 복사 -> 새 항목 추가
import React, { useState } from 'react';
function GoodCounter() {
const [items, setItems] = useState([1, 2, 3]);
const addItem = () => {
// 새 배열을 생성하고 기존 항목을 복사한 후 새 항목 추가
setItems([...items, items.length + 1]); // 불변성 유지
};
return (
<div>
<button onClick={addItem}>Add Item</button>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}