일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자료구조
- 보조저장장치
- react
- 유튜브 클론코딩
- 모던 자바스크립트 Deep Dive
- 자바스크립트
- MongoDB
- 표현식과 문
- 컴퓨터 구조
- 연결자료구조
- 마이크로명령어
- node.js
- Session
- 깃허브 로그인
- 제어유니트
- 프론트엔드
- JavaScript
- pug
- 후위표기법변환
- mongoose
- 컴퓨터 구조론
- Nodemon
- 리액트
- cookie
- 제어유닛
- 유튜브클론코딩
- 후위표기법연산
- Express
- CPU
- 자바스크립트 배열
- Today
- Total
909 Devlog
[React] state, useState 본문
👋 state, useState를 알아보기 전에, 간단한 예제를 한번 봅시다.
const Example = () => {
return (
<div>
<span>0</span>
<button>버튼</button>
</div>
);
};
export default Example;
컴포넌트에 span과 button이 존재합니다.
버튼을 클릭하면 span에 있는 숫자를 1씩 커지게 하고 싶은데, 어떻게 하면 좋을까요?
일단 막 생각나는 대로 한번 작성해 보겠습니다.
const Example = () => {
let index = 0;
return (
<div>
<span>{index}</span>
<button onClick={() => index++}>버튼</button>
</div>
);
};
export default Example;
index를 let 변수로 선언하고 0으로 초기화했고
span에는 index를 렌더링 하도록 {} (중괄호)에 씌워서 표시했으며
button이 클릭됐을 때는 index를 1 증가시키는 함수를 작성했습니다.
이렇게 하면 될 것 같지 않나요?
한번 버튼을 눌러보겠습니다 👌
숫자가 생각대로 증가되지 않네요 😅
📌 state란?
컴포넌트에서는 상호 작용의 결과로 화면의 내용을 변경해야 하는 경우가 있습니다.
input에 값을 입력한다던지, 버튼을 누르는 경우 등을 말합니다.
React에서는 이런 종류의 컴포넌트별 메모리를 state라고 부릅니다.
간단히 말해 state는 변수처럼 사용할 수 있는데요, 리액트에게 state에 할당한 데이터를 기억하라고 말하는 겁니다.
개념을 읽는다고 해서 이해가 되는건 아니니, 그냥 한번 써 봅시다.
👨💻 state 사용 방법
state는 react 모듈에서 useState를 불러와서 사용합니다.
import { useState } from "react";
useState를 불러왔다면, 바로 사용해 봅시다!
일반 변수처럼 할당하고, 괄호 안에 초기값을 넣어줬습니다.
위 예시와 같은 변수명인 index를 사용할 거예요.
그리고 button에 있는 onClick을 잠시 지워주도록 하겠습니다.
const index = useState(0);
import { useState } from "react";
const Example = () => {
const index = useState(0);
return (
<div>
<span>{index}</span>
<button>버튼</button> // onClick을 지웠습니다.
</div>
);
};
export default Example;
위와 같은 코드를 사용해서 렌더링 했더니, 콘솔에 오류가 떴습니다.
흠.. 어디서 오류가 났을까요? 🤔
사실 state는 배열입니다.
따라서 console.log(index)를 해보면 다음과 같이 배열이 뜨게 됩니다.
배열을 그냥 화면에 렌더링 하려고 했으니 리액트에서 경고를 띄운 것 이죠.
배열 내부에는 2가지 요소가 존재하는데,
- 첫 번째로는 담고 있는 데이터,
- 두 번째로는 해당 데이터를 변경시키기 위한 설정자(setter) 함수
를 담고 있습니다.
따라서 아래 코드와 같이 index [0]을 사용하면 오류 없이 데이터를 띄울 수 있습니다!
import { useState } from "react";
const Example = () => {
const index = useState(0);
return (
<div>
<span>{index[0]}</span>
<button>버튼</button>
</div>
);
};
export default Example;
그런데, state를 사용하기 위해 매번 뒤에 [0]를 붙여줘야 할까요? 너무 귀찮고 비효율적이지 않나요?
이럴 때를 위해, 자바스크립트에는 구조 분해 할당이란 게 존재합니다. 왼쪽 링크는 MDN 문서 링크이니 한 번 보고 오시는 것을 추천드립니다.
그럼 이제 state를 아래와 같이 사용할 수 있습니다.
const [index, setIndex] = useState(0);
첫 번째 요소인 데이터를 index라고 선언하고, 두 번째 요소인 설정자(setter) 함수를 setIndex라고 선언했습니다.
함수의 이름은 첫 번째 요소의 이름과 관계없이 마음대로 하셔도 상관없지만, "set(데이터 이름)"이라고 이름을 짓는 게 관습입니다.
그럼 제 목표인 "버튼을 누르면 span에 있는 숫자를 증가시키고 싶어"를 달성해 봅시다.
잠시 지웠었던 onClick을 다시 작성해 봅시다.
import { useState } from "react";
const Example = () => {
const [index, setIndex] = useState(0);
return (
<div>
<span>{index}</span>
<button onClick={() => index++}>버튼</button>
</div>
);
};
export default Example;
이제 제대로 작동할까요?
또 에러가 생겼습니다... 😅
근데 에러 문구가 익숙하지 않나요?
"Assignment to constant variable"
일반 자바스크립트를 사용할 때 한번 보셨지 않나요?
맞습니다.
const로 선언된 변수를 변경하려고 할 때 생기는 에러입니다.
우리가 구조 분해 할당으로 state의 데이터를 const로 선언했기 때문에 에러가 발생한 것입니다.
그럼 let으로 선언해야 할까요? 🤔
아니요. ❌
제가 배열에 요소가 2가지 존재한다고 말했던 것 기억나시나요?
두 번째 요소는 첫 번째 요소를 변경시키기 위한 함수라고 했었습니다.
state는 index = 100과 같은 변수 형식의 할당을 막기 위해 const로 선언하며, 그 const 변수를 바꿀 수 있는 state의 두 번째 요소, 설정자 함수가 존재합니다.
바로 사용해 보러 갑시다.
*주의* 설정자 함수의 괄호 내부에 값을 할당(index++, index += 1 등)하려고 하면 좀 전에 봤던 오류가 계속 뜰 것입니다.
대신, index + 1과 같이 변경하려고 하는 값을 넣어주세요.
값을 할당하기 위해 설정자 함수가 존재하는겁니다.
import { useState } from "react";
const Example = () => {
const [index, setIndex] = useState(0);
return (
<div>
<span>{index}</span>
<button onClick={() => setIndex(index + 1)}>버튼</button> // ++가 아닌 + 1임을 유의
</div>
);
};
export default Example;
드디어 성공했습니다!
📌 state를 사용할 때 알아야 할 것, 유의해야 할 점
🎯 설정자 함수를 사용하는 두 가지 방법
setIndex(index + 1);
setIndex((current) => {
return current + 1
})
첫 번째 방법인 설정자 함수의 괄호에 직접 변경할 값을 넣는 방법과, 두 번째 방법인 함수를 넣는 방법이 있습니다.
현재 값을 기반으로 state를 변경하고자 하는 경우에는 두 번째 방법인, 함수를 넣는 방법을 권장합니다.
🎯 useState 배열의 두 번째 요소인 설정자 함수를 호출하면 해당 함수가 있는 컴포넌트가 다시 렌더링 됩니다.
예시를 통해 알아봅시다.
import { useState } from "react";
const Example = () => {
const [index, setIndex] = useState(0);
console.log("렌더링"); // 추가
return (
<div>
<span>{index}</span>
<button onClick={() => setIndex(index + 1)}>버튼</button>
</div>
);
};
export default Example;
이전 코드에서 console.log("렌더링"); 만 추가했습니다.
페이지를 새로고침 하면 컴포넌트가 렌더링 되고, 컴포넌트 내부에 기본적으로 작성되어 있는 함수는 바로 실행되니 콘솔에 "렌더링"이 뜨게 됩니다.
첫 렌더링이 완료된 후 버튼을 누르면 useState의 설정자 함수가 호출되어 state의 값을 변경하게 됩니다.
state의 값이 변경되면 컴포넌트는 바뀐 state 값을 표시하기 위해 다시 렌더링 될 것입니다. 따라서 페이지를 처음 렌더링한 것과 같이 콘솔에 "렌더링"이 뜨게 됩니다.
🎯 다른 컴포넌트에서 같은 이름의 state가 존재해도 state는 지역 변수이기 때문에 충돌하지 않습니다.
import Example from "./test copy";
function App() {
return (
<div className="App">
<Example />
</div>
);
}
export default App;
이전에는 위 코드와 같이 Example 컴포넌트를 하나만 사용했었습니다.
import Example from "./test copy";
function App() {
return (
<div className="App">
<Example />
<Example />
<Example />
</div>
);
}
export default App;
이 코드처럼 컴포넌트를 3개로 늘리면 어떻게 될까요?
내부의 index가 모두 같은 값을 갖게 될까요?
같은 이름의 state라도, state는 지역 변수이기 때문에 모두 개별 값을 갖게 됩니다.
🎯 state는 다양한 type의 데이터를 가질 수 있습니다.
const [index, setIndex] = useState(0);
const [imString, setImString] = useState("문자열입니다");
const [imBoolean, setImBoolean] = useState(true);
const [imAry, setImAry] = useState(["후라이드", "양념"]);
const [imObj, setImObj] = useState({
치킨: "맛있다",
가지무침: "싫어요",
});
🎯 배열, 객체 state의 설정자 함수 사용 방법
위에서 배열과 객체를 state에 할당했었습니다.
배열과 객체의 요소를 변경하기 위해서는
<button onClick={() => {
setImAry(imAry[0] = "간장");
}}
>배열 버튼
</button>
이렇게 하면 안 되고
<button
onClick={() => {
const tempAry = [...imAry];
tempAry[0] = "간장";
setImAry(tempAry);
}}
>배열 버튼
</button>
이렇게 해야 합니다.
위에서 말했듯이, 설정자 함수의 괄호 내부에 값을 할당하는 할당문을 넣으면 안됩니다.
그 대신 변경하고 싶은 값을 넣으라고 했었죠?
imAry, imObj 자체가 변해야 리액트에서 감지할 수 있기 때문에 위 코드처럼 임시 변수를 만들어서 임시 변수의 요소를 바꾼 뒤 설정자 함수에 변수를 전달해야 데이터를 모두 변경합니다.
코딩애플 강좌에서 좀 더 자세한 설명을 보실 수 있습니다.
🎯 리액트는 가상돔을 사용해 요소를 변경합니다.
위 GIF를 보면 후라이드 -> 간장, 맛있다 -> 너무너무 맛있다 부분만 부분적으로 변경되는 것을 볼 수 있습니다.
state가 변경되면 해당 컴포넌트가 다시 렌더링 되긴 하지만, 모든 요소가 다 다시 렌더링 되는 것은 아닙니다.
리액트는 가상돔을 사용해서 변경된 부분만 따로 렌더링해 성능을 향상합니다.
본 포스팅에 사용한 코드입니다.
import { useState } from "react";
const Example = () => {
const [imAry, setImAry] = useState(["후라이드", "양념"]);
const [imObj, setImObj] = useState({
치킨: "맛있다",
가지무침: "싫어요",
});
return (
<div>
<ul>
<li>{imAry[0]}</li>
<li>{imAry[1]}</li>
</ul>
<h1>{imObj.치킨}</h1>
<h1>{imObj.가지무침}</h1>
<button
onClick={() => {
const tempAry = [...imAry];
tempAry[0] = "간장";
setImAry(tempAry);
}}
>
배열 버튼
</button>
<button
onClick={() => {
const tempObj = { ...imObj };
tempObj.치킨 = "너무너무 맛있다";
setImObj(tempObj);
}}
>
객체 버튼
</button>
</div>
);
};
export default Example;
'React > 개념' 카테고리의 다른 글
[Redux] subscribe (0) | 2023.09.07 |
---|---|
[React] Reducer (1) | 2023.09.06 |