원지의 개발
article thumbnail
728x90

state

  • 리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미
  • state가 체인지 되면 변화를 감지하여 리렌더링을 수행
  • 클래스 vs 함수형 컴포넌트에서 사용하는 방법이 다름
  • 함수형 컴포넌트에서는 훅 Hook 개념을 이용해서 더욱 쉽게 사용 가능
state는 어떤 타입이건 상관x (str, number, object)
state는 여러개 가능
state는 직접 수정x (setter 사용) 강제로 수정하면 리렌더링이 들어가지 X

useState(초기값) - 함수형 컴포넌트

const [data(데이터), setData(함수)] = useState('초기값')
  • 배열 반환
  • 첫번쨰 배열 요소: 현재값=데이터값
  • 두번째 배열 요소: 상태를 변경하는 (setter) 반환
  • useState: 데이터의 초기값

 

실습

App.js

import StateComponent from "./component/StateComponent";
import StateComponentQ from "./component/StateComponentQ";

const App = () => {

    return (
        <>
            <StateComponent/>
            <StateComponentQ/>
        </>
    )

}

export default App;

 

stateComponent.js

  • useState로 data='초기값'으로 설정 후
  • return에서 버튼을 클릭하면 setData()로 변경으로 바꿈
    함수 따로 안 만들고 onClick에 직접 넣어줌

  • 스타일을 직접 넣어주려면 style = { }안에 { } 사용 해야 함
  • 우선 color라는 state 만들고, 초기값 red로 정해줌
  • color의 데이터를 스타일의 컬러 속성에 넣음
  • state의 data값은 위에서 사용한 state이며, state는 여러개 사용가능
  • 버튼을 누를때마다 setColor가 레드 블루 옐로우로 바뀌면서 스타일 속성이 바뀜
import { useState } from "react";

const StateComponent = () => {

    /* 
        state란 컴포넌트에서 변화하는 값을 의미
        state가 변경되면 컴포넌트를 리렌더링 시킴
        함수형 컴포넌트에서는 useState()를 사용함
    */
    
    // 함수형 컴포넌트 useState()를 사용합니다.
    // let data = useState('초기값'); 콘솔로 찍었을때 data는 배열
    // 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 함수
    let func = () => { //함수 만들어서 변경
        setData('변경');
        console.log(data);
    }
    let enter = () => setData('입장');
    let exit = () => setData('퇴장');


    //state는 여러 개일 수 있습니다.
    let [color, setColor] = useState('red');
    

    // data = '1'; 이런식으로 강제로 바꾸면 리렌더링이 안 들어감 - 에러가없다는 화면을 다시 그린게 아님 !절대안됨!

    return (
        <>
            <h3 style={{'color': color}}>state 값: {data}</h3> {/* 왼쪽은 속성명 color, 오른쪽은 위의 state의 color */}
            <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('yellow') }>그 사이 3초 그 짧은 색인 노랑색</button>
        </>
    )

}

export default StateComponent;

stateComponentQ.js

import { useState } from "react";


const StateComponentQ = () => {

    //const로 선언하면 직접 값을 바꾸는 것 불가능
    //++count로 state를 직접 수정하면 안됩니다.
    const [count, setCount] = useState(0); //state, set state

    return (
        <div>
        <h3>실습</h3>
        <h3>카운트: {count}</h3>
        
        {/* 항상 state 값을 setter 메서드를 이용해야 함 */}
        <button onClick={ () => setCount(count+1)}>증가</button>
        <button onClick={ () => setCount(count-1)}>감소</button>
        <button onClick={ () => setCount(0)}>초기화</button>
        </div>
    )

}

export default StateComponentQ;
  • count의 초기값이 0인 state생성
  • 버튼 클릭시 setCount를 count+1로 현재 카운트에서 증가시킴
  • 단, count++로 state를 직접 수정하면 리렌더링이 안됨
  • 버튼 클릭시 setCount를 count-1로 현재 카운트에서 감소시킴
  • 버튼 클릭시 setCount를 count를 0으로 초기화시킴

class형에서의 state의 사용

실습

App.js

import { Fragment } from "react";
import MyComponent from "./component/MyComponent";
import MyComponent2 from "./component/MyComponent2";
import MyComponent3 from "./component/MyComponent3";

/* ES6 문법 */
const App = () => {
    return (
        <Fragment>
        <h3>App.JS</h3>
        <MyComponent /> {/* 하나로 끝나는 태그, 재활용 가능 */}
        {/* <MyComponent /> 주석처리 가능 */}
        {/* props: 하위 컴포넌트로 전달되는 데이터 값 */}
        <MyComponent name={'홍길동'} age={20} email={'aaa@naver.com'} /> {/* (1) 상위 컴포넌트에서 값을 넣고 */}
        <MyComponent2 name={'이순신'} />
        <MyComponent2 name={'홍길자'}/>

        {/* 클래스형 props */}
        <MyComponent3 name={'세종대왕'}/>
        </Fragment>
    )
}

export default App;

MyComponent3.js

import { Component } from "react";


class MyComponent3 extends Component { /* 리액트에서 제공하는 Component라는 모듈을 상속 받아야 함 */
    /* 
        state는 생성자 안에서 초기화를 합니다.
        state의 접근은 this.state를 이용해서 접근합니다.
        state는 반드시 객체모형 이어야 합니다.

        클래스형에서는 생성자를 작성할 때 반드시 props를 받고, super를 통해서 부모 컴포넌트에 연결해야 합니다.
    */
    constructor(props) { //이렇게 해줘야 error를 피할 수 있음
        super(props);
        this.state = { //this.state는 반드시 객체 모형이어야 함
            a: 1,
            b: props.name //부모로부터 전달받은 name
        }
    }


    //클래스형 컴포넌트를 render 함수 안에서 return문을 작성
    render() {

        let {name} = this.props; //props
        // console.log(name);
        
        return (
            <>
                <hr/>
                <div>아... 이건 클래스형 컴포넌트</div>
                state값: {this.state.a};
                state값: {this.state.b};
            </>
        )
    }

}

export default MyComponent3;

클래스형은 다시 확인


react 이벤트 핸들링

  • 이벤트 이름은 전부 카멜 표기법
  • 이벤트를 전달할 때는 { 함수 } 형태로 사용

출처: 쌤 티스토리

input 값 핸들링 - 실습 EventComponent.js

  • input 데이터가 바뀔 때 handleName 함수 실행
  • handleName 함수에서는 첫번째 매개변수에 이벤트 객체를 넣고 확인
  • 3번의 target: input, target.value = input에 적은 값이 나옴

  • name을 저장할 useSate 생성 - 초기값 ' '
  • input이 변경될 때 handleName 이벤트 함수가 실행되면서 name이 input = e.target의 value 값으로 저장 = 사용자가 입력한 값
  • input의 value={name} 적어줘야 함
    클래스형 컴포넌트에서는 value를 주지 않으면 값이 나오지 않기 때문에
    화면에 출력해주려고

  • onKeyUp 이벤트를 넣어주고 확인해보면 key, keyCode 나옴
  • if 조건문으로 e.keyCode === 13 이면 이벤트 호출

실습

App.js

import EventComponent from "./component/EventComponent";
import EventComponent2 from "./component/EventComponent2";
import EventComponentQ from "./component/EventComponentQ";
import EventComponentQ2 from "./component/EventComponentQ2";

const App = () => {

    /* 
        p.121
        이벤트 핸들링
        함수형 이벤트 핸들링
    */

    return (
        <>
        <EventComponent/>
        <EventComponent2/>

        {/* 실습 */}
        <EventComponentQ/>
        <EventComponentQ2/>
        </>
    )
}

export default App;

EventComponent.js

 

 

 

import { useState } from "react";


const EventComponent = () => {

    //name을 저장할 useState
    const [name, setName] = useState('');

    //이벤트 함수의 첫번째 매개변수에 이벤트에 대한 정보를 넣어줍니다.
    const handleName = (e) => { /* 클래스형 컴포넌트에서는 this를 막 쓸수없음, this가 가르키는 대상이 달라지기 때문 */
        // console.log(e.target.value);
        setName(e.target.value); //state change
    }

    //topic
    const [topic, setTopic] = useState('');
    const handleTopic = (e) => { /* 함수의 첫번째 매개변수로 이벤트 객체를 받아줌 */
        setTopic(e.target.value);
    }

    //클릭 이벤트
    const handleClick = (e) => {
        alert(`${name}님의 주제는 ${topic}입니다`); //name과 topic은 현재 가리키고 있는 state 값
        setName(''); //input 데이터 초기화
        setTopic(''); //input 데이터 초기화
    }

    //엔터키의 처리
    const handlePress = (e) => {
        // console.log(e);
        if(e.keyCode === 13) { //엔터값
            handleClick(); //클릭이벤트 호출
        }
    }

    return(
        <>
            <h3>리액트의 이벤트 핸들링 (input데이터)</h3>
            <input type="text" name="name" onChange={handleName} value={name}/>{/* change이벤트-handleName함수 */}
            <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;
  • 버튼을 눌러주면 경고창이 뜨면서 input에 적은 이름과 주제를 보여줌
  • 우선 name과 topic을 저장할 useState을 만듦
  • name, topic에 onChange 이벤트가 걸리면 바로 value값을 나타내 주기 위해서 {name}, {topic}을 넣어줌
    value도 넣어줘야 화면에 뿌려짐
  • 각각의 input에 onChange 이벤트 걸기
    함수의 첫번째 매개변수로 이벤트 객체를 받아주고, set으로 e.target.value = input에 적은 데이터값을 저장해줌
  • 클릭이벤트는 onKeyUp으로 엔터의 키값이 13인데, e.keyCode = 13이면 클릭이벤트를 호출하여 실행되게 함

EventComponent2.js - 객체로

import { useState } from "react";
import EventComponent from "./EventComponent";


const EventComponent2 = () => {

    //state를 객체로 관리
    const [data, setData] = useState({name: '', topic: ''}); //state는 하나임

    const handleChange = (e) => {
        // console.log(e.target.name); //name 나옴
        // console.log(data) //{name: '', topic: ''} = state는 객체
        // 객체 안에서 key를 바꾸는 방법 ["키"] : 값
        const copy = {...data, [e.target.name]: e.target.value }; //데이터 복사 ▶ name의 값을 사용자가 입력한 값으로 바꾸기
        // console.log(copy);
        setData(copy); //name state 변경
    }
    
    /* 
        const handleTopic = (e) => {
            // console.log(e.target.name); //topic 나옴
            const copy = {...data, ["topic"] : e.target.value};
            setData(copy); //topic state 변경
        }
    */

    const handleClick = (e) => {
        alert(`${data.name}님 할 일: ${data.topic}`); //객체니까 data.name, data.topic
        setData({name: '', topic: ''}); //다시 초기화
    }

    return(
        <>
            <h3>리액트 이벤트 핸들링 (객체로)</h3>

            <input type="text" name="name" onChange={handleChange} value={data.name}/>
            <h3>결과: {data.name}</h3>

            <input type="text" name="topic" onChange={handleChange} value={data.topic}/>
            <h3>결과: {data.topic}</h3>

            <button type="button" onClick={handleClick}>click me</button>
        </>
    )

}

export default EventComponent2;
  • eventComponent와 다른점은 각각의 값을 useState로 가져오는 것이 아닌 하나의 객체로 가져옴
  • useState를 객체로 관리 ▶ useState({name: ' ', topic: ' '})
  • 객체로 가져왔기 때문에 화면에 나타낼때도 .name, .topic 이런식으로 사용해야 함
객체 안에서 key를 바꾸는 방법
["키"] : 값
  • input 값이 바뀌면 setData로 바뀐 값으로 저장해줘야 하는데 name 하나만 혹은 topic 하나만 바뀔 수 있어서 한꺼번에 바뀌면 안됨
  • 그래서 copy 객체를 만드는데 ...data로 원래 객체를 가져오고, key값을 바꾸기 위해 [e.target.name] : e.target.value
    여기서 name은 e.target = input 태그의 속성 name

EventComponentQ.js

import { useState } from "react";


const EventComponentQ = () => {

    const [menu, setMenu] = useState('메뉴를 선택하세요');

    const handleChange = (e) => { //첫번째 매개변수는 event객체를 받겠다
        if(e.target.value === '선택') { //선택을 누르면 아무것도 안 나오게 조건문
            setMenu('메뉴를 선택하세요');
        } else {
            setMenu(e.target.value);
        }
    }

    return(
        <>
        <hr/>
        <h3>셀렉트 태그 핸들링 (실습)</h3>
        <p>셀렉트 태그가 체인지될 때 선택한 결과를 아래에 출력</p>
        
        <select onChange={handleChange}> {/* select 태그는 옵션이 value */}
            <option>선택</option>
            <option>햄버거</option>
            <option>피자</option>
            <option>치킨</option>
        </select>

        <h3>선택한 결과</h3>
        <h4>{menu}</h4>
        </>
    )

}

export default EventComponentQ;
  • select의 option이 바뀌면 바뀐 결과가 화면에 뜨게 하기
  • useState로 menu 관리, {menu}로 화면에 띄워줌
  • select의 이벤트 객체로 첫번째 매개변수는 event 객체를 받아서
    if조건문: e.target = select, e.target.value = option 이 선택이면 메뉴를 선택하세요가 나오고
                  그게 아니면 setMenu로 menu에 옵션을 저장

useState 하나로 관리 (객체로 사용)

  • EventComponent2.js -객체로 //실습파일과 같음
  • ...form: 데이터 복사
    name: "홍길동" = ["name"]: "홍길동
    ▶ name 값만 변경하기 위해 사용
    이렇게 하는 이유는 데이터 값이 많을 때 나머지는 그대로 유지, 복사 해와서 필요한 값만 바꾸기 위해서

EventComponentQ2 내 답 - state 두개 사용

import { useState } from "react";


const EventComponentQ2 = () => {

    const [data, setData] = useState('');
    const handleChange = (e) => {
        console.log(e.target.value); //사람이 적은 값
        setData(e.target.value);
    }
    
    const [change, setChange] = useState('');
    const handleAdd = (e) => {
        console.log(e.target); //button나옴
        const copy = [...data, e.target.value];
        setChange(copy);
        setData('');
    }

    return(
        <>
        <hr/>
        <h3>인풋데이터 핸들링 (실습)</h3>
        <p>클릭시 데이터는 공백으로 결과는 인풋이 입력한 값으로 처리</p>
        <p>힌트는? 아마도 state 두 개가 필요할 듯?</p>

        <input type="text" name="name" onChange={handleChange} value={data}/> {/* state는 공백인데, value값이 없으면 그게 화면에 나타나지 않음 */}
        <button onClick={handleAdd}>추가하기</button>

        <h3>결과</h3>
        <h4>{change}</h4>
        </>
    )
}

export default EventComponentQ2;
  • input의 value인 {data}와 결과값{change}을 state로 관리

EventComponentQ2 다른 답 - 객체 사용

import { useState } from "react";


const EventComponentQ2 = () => {

    const[change, setChange] = useState({input: '', result: ''});

    const handleChange = (e) => {
        const copy = {...change, [e.target.name]: e.target.value};
        setChange(copy); //인풋이 바뀌면 input에 입력된 데이터를 저장해서 코드를 복사해서 를 저장한다
       
    }
    const handleClick = () => {
        setChange({input: '', result: change.input}) 
        //버튼을 클릭하면 input에 입력된 값을 result에 저장하고 input은 공백으로 변경 
    
    }


    return (
        <>
        <h3>인풋데이터 핸들링(실습)</h3>
        <p>클릭시 데이터는 공백으로 결과는 인풋이 입력한 값으로 처리 <br/>
            리엑트 힌트는? 아마도 state는 두개가 필요할 듯? 
        </p>

 
        <input type="text" onChange={handleChange} name="input" value={change.input}></input>
        <button type="button" onClick={handleClick} name="ressult">추가하기</button>

        <h3>결과: {change.result}</h3>
    
        </>
    )

}
export default EventComponentQ2;

EventComponentQ2 강사님 답1 - useState 2개 사용

import { useState } from "react";

//강사님 답 1st
const EventComponentQ2 = () => {

    const [data, setData] = useState(''); //인풋데이터
    const [result, setResult] = useState(''); //결과데이터

    const handleChange = (e) => {
        setData(e.target.value); //사용자가 입력한 값 data에 저장, 비동기적으로 변경(늦게 나오는게 정상)
        //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}/> {/* 핸들링 되는 data(input값) 표현해줘야 함 */}
        <button onClick={handleClick}>추가하기</button>

        <h3>결과</h3>
        <h4>{result}</h4>
        </>
    )
}
export default EventComponentQ2;
  • input 데이터와 결과 데이터를 state로 관리
  • input에서 변경될 때 setData로 사용자가 입력한 값 data에 저장
  • 버튼을 눌렀을 때 setResult로 data를 result에 저장하고 data는 공백 처리

EventComponentQ2 강사님 답2 - 객체 사용

import { useState } from "react";

//강사님 답 2nd
const EventComponentQ2 = () => {

    //state를 객체로 관리
    const [form, setForm] = useState({data: '', result: ''});

    const handleChange = (e) => {
        //data는 사용자의 입력값으로, result는 유지(form.result로 그대로 가져오기)
        setForm({data: e.target.value, result: form.result});
    }
    
    const handleClick = (e) => {
        //data는 '', result는 data로 변경
        setForm({data: '', result: form.data});
    }

    return(
        <>
        <hr/>
        <h3>인풋데이터 핸들링 (실습)</h3>
        <p>클릭시 데이터는 공백으로 결과는 인풋이 입력한 값으로 처리</p>
        <p>힌트는? 아마도 state 두 개가 필요할 듯?</p>

        <input type="text" name="name" onChange={handleChange} value={form.data}/>
        <button onClick={handleClick}>추가하기</button>

        <h3>결과</h3>
        <h4>{form.result}</h4> {/* 처음에는 공백이지만 나중에는 바뀔 값 */}
        </>
    )
}
export default EventComponentQ2;
  • 객체(data, result 값)를 useState로 관리
  • input의 value값이 변경될 때 setForm으로 {data: e.target.value=사용자가 입력한 값, result: form.result=state에 저장된 result값} ▶ 아직 result값을 ' ' 공백
  • 버튼을 클릭하게 되면 setForm으로 {data: ' ' 공백처리, result: form.data}로 결과값은 data에 저장된 내용(사용자가 입력한 값) 저장

오늘 하루

더보기

기억에 남는 부분

 

어려운 부분

 

문제 해결 부분

728x90
profile

원지의 개발

@원지다

250x250