state
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다
state가 체인지 되면 변화를 감지하여 리렌더링을 수행한다
클래스 vs 함수형 컴포넌트에서 사용하는 방법이 다르다
함수형 컴포넌트에서는 Hook 개념을 이용해 더욱 쉽게 사용 가능하다
리액트에는 두 가지 종류의 state가 있다
1️⃣ 클래스형 컴포넌트가 지니고 있는 state
2️⃣ 함수 컴포넌트에서 useState라는 함수를 통해 사용하는 state
//1st
let data = useState('초기값');
console.log(data); // 배열이기 때문에 다음과 같이 출력
let a = data[0]; - 현재값
let b = data[1]; - 함수
//2nd
let [data, setData] = useState('초기값');
console.log(data); //state값
console.log(setData); //state setter함수
data = '1'; 👉 직접 값을 바꾸게 되면 값이 바뀌어서 나타나지만 나중에 문제 발생할 수 있음
⚠️ 다음과 같이 오류가 발생한다!
⭐ state값은 직접 변경하는 것이 불가능하고 변경하려면 setter 함수 이용해야 한다
setData('변경할값');
const StateComponent = () => {
let [data, setData] = useState('초기값');
let func = () => setData("변경");
let enter = () => setData("입장");
let exit = () => setData("퇴장");
// 값을 변경할 때마다 렌더링이 일어나고 있다!
return (
<>
<button onClick={func}> 값 변경하기 </button>
<button onClick={enter}> 입장 </button>
<button onClick={exit}> 퇴장 </button>
</>
)
}
👩💻 함수 내부에 state는 여러 개 존재할 수 있다
const StateComponent = () => {
let [data, setData] = useState('초기값');
let func = () => setData("변경");
let enter = () => setData("입장");
let exit = () => setData("퇴장");
// 값을 변경할 때마다 렌더링이 일어나고 있다!
// state는 여러개일 수 있다
let [color, setColor] = useState("pink");
console.log(color);
return (
<>
<h3 style={{ 'color': color }}> state값 : {data} </h3>
<button onClick={func}> 값 변경하기 </button>
<button onClick={enter}> 입장 </button>
<button onClick={exit}> 퇴장 </button>
<hr />
<button onClick={() => setColor("red")}>붉은색</button>
<button onClick={() => setColor("blue")}>푸른색</button>
<button onClick={() => setColor("green")}>그 사이 3초 그 짧은색</button>
</>
)
}
export default StateComponent;
클래스형 컴포넌트의 state
< Counter.js >
import { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0
};
}
render( ){
const { number } = this.state; // state를 조회할 때는 this.state로 조회한다
return (
<div>
<h1>{number}</h1>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있다
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
컴포넌트에 state를 설정할 때는 constructor 메서드를 작성해 설정한다
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0
};
}
render 함수에서 현재 state를 조회할 때는 this.state를 조회하면 된다
그리고 button 안에 onClick이라는 값을 props로 넣어주었는데, 이는 버튼이 클릭될 때 호출시킬 함수를 설정할 수 있게 해준다 (이벤트 설정)
함수 내부에서 this.setState라는 함수를 사용하는데 이는 state값을 바꿀 수 있게 해준다
render( ){
const { number } = this.state; // state를 조회할 때는 this.state로 조회한다
return (
<div>
<h1>{number}</h1>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정한다
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있다
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
함수형 컴포넌트의 State
const StateComponentQ = () =>{
// < 내 답안 >
// let [number, setNumber] = useState("0");
// let plus = () => setNumber(number+1);
// let minus = () => setNumber(number-1);
// let reset = () => setNumber(0);
// return(
// <>
// <h3>카운트: {number}</h3>
// <button onClick={plus}>증가</button>
// <button onClick={minus}>감소</button>
// <button onClick={reset}>초기화</button>
// </>
// )
// < 선생님 답안 >
const [number, setNumber] = useState("0"); // number++로 state를 직접 수정하는 것은 불가능
return(
<>
<h3>카운트: {number}</h3>
<button onClick={() => setNumber( number + 1 )}>증가</button>
<button onClick={() => setNumber( number - 1 )}>감소</button>
<button onClick={() => setNumber(0)}>초기화</button>
</>
)
}
export default StateComponentQ;
const [number, setNumber] = useState("0"); // number++로 state를 직접 수정하는 것은 불가능
🙋♀️ 그런데 const키워드로 선언해서 값을 직접 수정하는 것이 불가능한 거야, 아님 state라서 그런 거야?
👩💻 state라서! state는 값을 직접 수정하게 되면 자동으로 렌더링이 일어나지 않게 돼
그래서 보통 state 값을 수정하지 못하도록 const 키워드로 변수를 선언하곤 하지!
이벤트 핸들링
이벤트 규칙
1️⃣ 이벤트의 이름은 전부 카멜 표기법으로 표현됩니다. onkeyup -> onKeyUp
2️⃣ 이벤트를 전달할 때는 {함수} 형태로 사용합니다
인풋값 핸들링 해보기
1️⃣인풋의 값이 변화되는 이벤트 onChange 연결
2️⃣ 이벤트 안 첫번째 매개변수에서 event객체 활용하기 (e.target.속성값)
3️⃣ setter를 이용해서 state변경하기
실습 전체 코드
userState 함수를 활용한 이벤트 핸들링
import { useState } from "react";
const EventComponent = () => {
//name을 저장할 useState
const [name, setName] = useState('');
//이벤트 함수의 첫번째 매개변수에 이벤트에 대한 정보를 넣어준다
const handleName = (e) =>{
// console.log(e.target.value);
setName(e.target.value); //state체인지
}
const [topic, setTopic] = useState('');
const handleTopic = (e) => {
setTopic(e.target.value);
}
const handleClick = () => {
alert(`${name}님의 주제는 ${topic}입니다`); //state값
setName(''); //인풋데이터 초기화
setTopic(''); //인풋데이터 초기화
}
//엔터키의 처리
const handlePress = (e) => {
console.log(e);
if(e.keyCode === 13){
handleClick();
}
}
return (
<>
<h3>리액트의 이벤트 핸들링 (인풋데이터)</h3>
<input type="text" name="name" onChange={handleName} value={name}/>
<div>체인지 된 결과: {name}</div>
<input type="text" name="topic" onChange={handleTopic} onKeyUp={handlePress} value={topic}/>
<div>체이지 된 결과 : {topic}</div>
<button type="button" onClick={handleClick}>Click Me</button>
</>
)
}
export default EventComponent;
< 하나하나 코드를 분석해보자! >
name을 저장할 useState( );
//name을 저장할 useState
const [name, setName] = useState('');
input태그에서 값이 변경될 때(값이 입력되면) 다음의 hadleName함수가 실행된다
//이벤트 함수의 첫번째 매개변수에 이벤트에 대한 정보를 넣어준다
const handleName = (e) =>{
// console.log(e.target.value);
setName(e.target.value); //state체인지
}
e.target.value 👉 사용자가 input태그에 입력한 값을 저장 ( state 체인지 )
const [topic, setTopic] = useState('');
const handleTopic = (e) => {
setTopic(e.target.value);
}
topic도 name과 같은 방식으로 설정
const handleClick = () => {
alert(`${name}님의 주제는 ${topic}입니다`); //state값
setName(''); //인풋데이터 초기화
setTopic(''); //인풋데이터 초기화
}
버튼을 클릭했을 때, 경고창이 나타나면 input 태그에 작성된 값이 초기화되게 설정했다
//엔터키의 처리
const handlePress = (e) => {
console.log(e);
if(e.keyCode === 13){
handleClick();
}
}
버튼을 클릭했을 때뿐 아니라 엔터키를 눌렀을 때에도 실행될 수 있도록 처리를 해주었는데, 이벤트는 onKeyUp을 사용하였고 엔터의 keyCode가13이기 때문에 e.keyCode가13일 때 handleClick함수가 실행되도록 해주었다
return (
<>
<h3>리액트의 이벤트 핸들링 (인풋데이터)</h3>
<input type="text" name="name" onChange={handleName} value={name}/>
<div>체인지 된 결과: {name}</div>
<input type="text" name="topic" onChange={handleTopic} onKeyUp={handlePress} value={topic}/>
<div>체이지 된 결과 : {topic}</div>
<button type="button" onClick={handleClick}>Click Me</button>
</>
)
state를 객체로 관리하여 이벤트 핸들링
import { useState } from "react";
const EventComponent2 = () =>{
//state를 객체로 관리
const [data, setData] = useState({name: '', topic: ''});
//한 개의 state로 여러 개의 데이터 관리
const handleChange = (e) =>{
//객체 안에서 key를 바꾸는 방법["키"] : 값
const copy = {...data, [e.target.name] :e.target.value}; //데이터 복사 👉 데이터 형식을 복사해서 데이터 내부의 키에 대한 값만 변경
setData(copy); //state 변경
}
/*
const handleTopic = (e) =>{
//객체 안에서 key를 바꾸는 방법["키"] : 값
console.log(e.target.name);
const copy = {...data, ["topic"] :e.target.value}; //데이터 복사 👉 데이터 형식을 복사해서 데이터 내부의 키에 대한 값만 변경
setData(copy);
}
*/
const handleClick = () =>{
alert(`${data.name}님 할 일: ${data.topic}`)
setData({name:'', topic:''}); //state초기화 (객체라서)
}
return (
<>
<h3>리액트 이벤트 핸들링(객체로)</h3>
<input type="text" name="name" onChange={handleChange} value={data.name}/>
<h5>결과: {data.name}</h5>
<br/> <input type="text" name="topic" onChange={handleChange} value={data.topic}/>
<h5>결과: {data.topic}</h5>
<br/><button type="button" onClick={handleClick}>ClickMe</button>
</>
)
}
export default EventComponent2;
구체적으로 코드를 하나씩 뜯어봅시다!
state를 객체로 관리할 것이기 { key : value }로 작성 - 한 개의 state로 여러 개의 데이터를 관리할 수 있음!
//state를 객체로 관리
const [data, setData] = useState({name: '', topic: ''});
//한 개의 state로 여러 개의 데이터 관리
input태그의 value가 입력되었을 때 실행될 함수 : name에 대한 함수
const handleChange = (e) => {
//객체 안에서 key를 바꾸는 방법["키"] : 값
const copy = {...data, [e.target.name] :e.target.value}; //데이터 복사 👉 데이터 형식을 복사해서 데이터 내부의 키에 대한 값만 변경
setData(copy); //state 변경
}
데이터 형식은 똑같은데 key에 대한 값만이 바뀔 때 위와 같이 사용하면 된다
input태그의 value가 입력되었을 때 실행될 함수 : topic 에 대한 함수
const handleTopic = (e) = >{
//객체 안에서 key를 바꾸는 방법["키"] : 값
console.log(e.target.name);
const copy = {...data, ["topic"] :e.target.value}; //데이터 복사 👉 데이터 형식을 복사해서 데이터 내부의 키에 대한 값만 변경
setData(copy);
}
name과 함수 기능이 동일하기 때문에 굳이 함수가 두 번 사용될 필요가없다 👉 그래서 하나의 함수로 해결
const handleClick = () => {
alert(`${data.name}님 할 일: ${data.topic}`)
setData({name:'', topic:''}); //state초기화 (객체라서)
}
버튼을 클릭했을 때 경고창을 띄우고, input 태그에 사용자가 작성한 값을 초기화 시켜주는 함수인데, state를 객체로 구현했기에 초기화 역시 위와 같이 초기화시켜야 한다
return (
<>
<h3>리액트 이벤트 핸들링(객체로)</h3>
<input type="text" name="name" onChange={handleChange} value={data.name}/>
<h5>결과: {data.name}</h5>
<br/> <input type="text" name="topic" onChange={handleChange} value={data.topic}/>
<h5>결과: {data.topic}</h5>
<br/><button type="button" onClick={handleClick}>ClickMe</button>
</>
)
name과 topic 모두 사용할 함수의 기능이 동일하기 때문에 handleChange라는 함수로 통일하여 메모리 낭비를 방지했다
나와의 싸움 Time
1️⃣ 셀렉트 태그 이벤트 핸들링
import { useState } from "react";
const EventComponentQ = () =>{
const [food, setFood] = useState("메뉴를 선택하셈");
//셀렉트 태그에서는 option태그가 기본 value가 된다
const change = (e) => {
setFood(e.target.value);
// console.log(e.target.value);
}
return (
<>
<hr/>
<h3>셀렉트 태그 핸들링(실습)</h3>
<p>셀렉트 태그가 체인지 될 때 선택한 결과를 아래에 출력</p>
<select onChange={change}>
{/* <option value="">선택</option> */}
<option>곱창</option>
<option>닭발</option>
<option>엽떡 오리지널</option>
<option>엽떡 로제</option>
</select>
<h3>선택한 결과</h3>
<p> {food}</p>
</>
)
}
export default EventComponentQ;
2️⃣ input 데이터 핸들링 - state를 객체로 관리
import { useState } from "react";
const EventComponentQ2 = () =>{
const [text, setText] = useState({input: '', result:''});
const handleClick = () => {
//INPUT은 공백, RESULT는 data로 변경
const copy = {...text, ['result'] : text.input, ['input']: ''};
//setText({input:'', result:text.input});
setText(copy);
}
const handleChange = (e) =>{
//INPUT은 사용자의 입력값으로, RESULT는 유지
const copy = {...text, ['input'] : e.target.value};
//setText({input:e.target.value, result:text.result});
setText(copy);
}
const handlePress = (e) => { //엔터키 처리
console.log(e);
if(e.keyCode === 13){
handleClick();
}
}
return(
<>
<hr/>
<h3>인풋데이터 핸들링(실습)</h3>
<p>클릭시 데이터는 공백으로, 결과는 인풋이 입력한 값으로 처리</p>
<p>힌트는? 아마도 state는 두 개가 필요할듯?</p>
<input type="text" name="name" onChange={handleChange} onKeyUp={handlePress} value={text.input}/>
<button type="button" onClick={handleClick}> 추가하기 </button>
<h3>결과</h3>
<p>{text.result}</p>
</>
)
}
export default EventComponentQ2;
2️⃣ input 데이터 핸들링 - 두 개의 state로 관리
import { useState } from "react";
const EventComponentQ2_answer = () =>{
const [data, setData] = useState(''); //인풋데이터
const [result, setResult] = useState(''); //결과데이터
const handleChange = (e) =>{
setData(e.target.value); // 비동기적으로 변경 (기존 state값이 출력)
// console.log(data); // 이전 값이 출력됨 (정상!)
}
const handleClick = (e) => {
setResult(data); // 사용자가 입력한 값으로 변경
setData('');
}
return(
<>
<hr/>
<h3>인풋데이터 핸들링(실습)</h3>
<p>클릭시 데이터는 공백으로, 결과는 인풋이 입력한 값으로 처리</p>
<p>힌트는? 아마도 state는 두 개가 필요할듯?</p>
<input type="text" name="name" onChange={handleChange} value={data}/>
<button type="button" onClick={handleClick}>추가하기</button>
<h3>결과</h3>
<p>{result}</p>
</>
)
}
export default EventComponentQ2_answer;
'React' 카테고리의 다른 글
[React] 라우터 (useNavigate, Navigate, Link 등) (1) | 2023.01.20 |
---|---|
[React] Hook(훅) - useState, useEffect, useRef, useReducer (0) | 2023.01.18 |
[React] 리액트의 Components와 props (0) | 2023.01.15 |
[React] JSX 문법 알고 싶으면 이리온 : ) (0) | 2023.01.15 |
[React] 리액트 다운로드하고 실행시키기 (0) | 2023.01.15 |