TIL 2023-01-04 리액트 state, hooks, event

JYR00·2023년 1월 4일
0

TIL

목록 보기
54/60

folder2 생성, LinkedButton.js생성, Notification.jsx

대부분 folder2에 있음

깃허브

오전시간 총 예제

State 사용하기

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App2';
import reportWebVitals from './reportWebVitals';
import App2 from "./App2";
import App3 from "./App3";
import App33 from "./App33";
import App5 from "./App5";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(

    // strict mode : 자바스크립트의 문법을 강력하게 확인한다는 의미. 자바스크립트는 간소화된 문법이다. 그렇지만 이게 있으면 좀 더 강하게 확인한다
    // React.StrictMode : 배포 버전에는 제외되고 개발 버전에서는 동작하는 엄격모드, 몇 가지 함수를 중복 실행 하여 잘못된 것이 없는지 개발자에게
    // 확인하도록 한다. 다 주석 처리해도 잘 실행된다
    // <React.StrictMode>

    //{/*/!*     strict 모드란 react 앱 내의 잠재적인 문제를 알아내기 위한 도구라고 나와있습니다.*!/,*/}
    //{/*/!*<App />*!/*/}
    //{/*/!*    <App2/>*!/*/}
    //{/*/!*    <App3/>*!/*/}
    //{/*/!*    <App33/>*!/*/}
    //{/*    */}

    //{/*/!*</React.StrictMode>*!/*/}
<App5/> <---!!!! 이거 사용한다
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App5.jsx

// App5.jsx

import React from "react";
import NotificationList from "./folder2/NotificationList";
import CountButton from "./folder2/CountButton";

function App5(){
    return (
        <div className = {"container"}>
            <NotificationList/>
            <CountButton/>
        </div>
    )
}
export default App5;

Notification.jsx

//Notification.jsx

import React from "react";

const styles = {
    wrapper : {
        margin : 8,
        padding : 8,
        display : "flex",
        flexDirection : "row",
        border : "1px solid grey",
        borderRadius : 16,
    },
    messageText :{
        color : "black",
        fontSize : 16,

    }
}

class Notification extends React.Component {
    constructor(props) {
        super(props);

        this.state = {}
    }

    render() {
        return(
            <div style = {styles.wrapper}>
                <span style = {styles.messageText}>
                    {this.props.message}
                </span>
            </div>
        );
    }
}

export default Notification;

NotificationList.jsx

// NotificationList.jsx

import React from "react";
import Notification from "./Notification";

// 서버에서 전송되어야 할 데이터, 서버가 없기 때문에 대체. 통신모듈이 없기 때문에 임의로 만들어준 데이터
const msgData = [
    {id : 1, message : "안녕하세요. 오늘 일정입니다"},
    {id : 2, message : "점심 식사 시간입니다."},
    {id : 3, message : "이제 곧 미팅이 시작됩니다"},
];

// 자바 스크립트 타이머. 객체 정보를 저장할 변수
let timer;

// 클래스 컴포넌트를 사용
class NotificationList extends React.Component {
    
    // 클래스 컴포넌트의 생성자 부분
    constructor(props) {
        super(props);

        // state 객체 선언 (멤버변수로 선언)
        // 클래스(객체)의 멤버 변수로 선언
        this.state = {
            // 메시지를 저장할 배열
            notification : [],
        };
    }

    

    // 화면을 생성(랜더링)하는 함수
    render() {
        return (
            <div>
                {/*map : es6버전에서 추가된 배열 관련 함수. 지정한 배열의 크기만큼 반복하고 결과를 배열로 반환하는 함수*/}
                {this.state.notification.map((item)=>{
                    return <Notification key={item.id} message = {item.message}/>
                })}
            </div>
        );
    }

    //생명주기 할려고 일부러 class로 만들었다고 함.
    // 컴포넌트가 마운드된 후 실행되는 생명주기 함수
    // 다 동작하고 난 다음(constructer -> render -> componentDidMount) 동작됨
    componentDidMount() {
        // object 타입을 사용한 확장 표현식
        const {notification } = this.state; //현재 둘 다 비어있다. state가 비어있으니까(notification : [])

        // 타이머를 사용하여 지정된 시간에 한 번 씩 반복함
        timer = setInterval(()=>{  //setInterval : 지정된 시간 동안 계속 반복.반복실행
            // 지역변수 notification 크기가 전역 변수인 배열 msgData의 크기보다 작을 경우 if문 실행됨
            
            // 자바스크립트는 one thread 방식, 비동기실행방식이다. 이벤트 큐에 넣어주고 자기는 떠나며 쿨타임 찰 때 까지 반복함
            if (notification.length < msgData.length) { //현재 notification.length는 0. => 방금 notification의 크기가 1이 됨 // 나중에 3이 되면 3과 3은 동일하니까 아래로 간다
                const index = notification.length; //현재는 0이 들어감 -> 현재 1이다
                notification.push(msgData[index]); //push로 가장 뒤에 msg의 데이터를 넣어준다

                // setState() 함수를 사용하여 state 객체를 수정함
                this.setState({  //state 수정. notification에 notification을 넣는다
                    notification : notification, //전역변수 notification : 여기서 선언한 지역변수 notification. // 넣어주기 때문에 1이 된다. // 2가 된다
                });
            }
            else {
                clearInterval(timer); // false가 되면 이곳으로 와서 timer를 삭제한다 -> 반복 끝남.
            }
        }, 1000); // 1초 후에 다시 실행됨
    }
}

export default NotificationList;

LinkButton.js

//LinkButton.js

import React from "react";


// state : 현재 컴포넌트의 상태를 나타내는 객체, 현재 컴포넌트에서 변경가능한 객체, state는 직접적인 수정이 불가능함(화면 렌더링과 관련이 있음)
// setState() : state의 값을 변경하는 함수

// react의 생명주기 : 컴포넌트가 생성되고 내용이 변경되고 컴포넌트가 삭제되는 상태
// componentDidMount : 컴포넌트가 생성되고 화면에 렌더링 된 이후 실행되는 함수
// componentDidUpdate : 컴포넌트의 상태가 변경된 후 실행되는 함수, props 변경, setState() 함수 실행
// forceUpdate()를 통한 강제 업데이트 후 동작함.
// componentWillUnmount : 부모 컴포넌트에서 더이상 해당 컴포넌트를 사용하지 않아 삭제된 후 실행

class LinkButton extends React.Component {
    constructor(props) {
        super(props);

        //state 설정
        this.state = {
            linked : false,
        }
        //현재 state 값 수정
        this.setState({
            linked : true, //이런 식으로 변경해준다
        })
    }
}

CountButton.jsx

// CountButton.jsx

import React from "react";
import Button from "react-bootstrap/Button";

class CountButton extends React.Component {
    constructor(props) {
        super(props);

        // state 객체 생성
        //setState()를 사용해야만 여기서 선언한 객체를 수정할 수 있다.
        this.state = {
            count: 0,
        }

        // 클래스의 멤버 변수(객체의 멤버 변수) count 선언 count: 0 가져옴
        this.count = this.state.count;
    }

    //메서드 2개 생성
    countUp = () => {
        // this.state.count = this.state.count + 1
        console.log(" 이전 this.count : " + this.count);
        this.count = this.count + 1;
        // setState() 함수를 실행해야만 state의 값이 수정이 되고, state의 값이 수정되면 render() 함수가 재실행됨
        this.setState({
            count : this.count
            }
        )
        console.log(" + 사용후 this.count : " + this.count);
    };

    countDown = () => {
        // this.state.count = this.state.count - 1
        console.log(" 이전 this.count : " + this.count);
        this.count = this.count - 1;
        // 화면의 count 숫자 바뀜
        this.setState({
            count : this.count
        })
        console.log("- 사용 후 this.count : " + this.count);
    };


    render() {
        return(
            <div>
            <label className={"form-label"}> count : <span>{this.count}</span></label><br/>
                <Button variant = {"primary"} onClick = {this.countUp}>+</Button>
                <Button variant = {"danger"} onClick = {this.countDown}>-</Button>
            </div>
        )
    }
}

export default CountButton;

setState()를 사용해야만 state 값을 변경할 수 있다 => 버튼 눌러서 count 값 바꿀려면 setState()를 사용해야만 했다.


Hooks 사용하기

CountButton2.jsx

// CountButton2.jsx
// hooks 예제

// useState() 훅을 사용하기 위해서 미리 import 해야 한다
import React, {useState, useEffect} from "react";

// hooks : 기존에 존재하는 기능을 추가로 함께 사용하도록 하는 것.
// state와 react 생명주기 기능을 원하는 시점에 실행할 수 있도록 하는 기능

// 우리는 useState useEffect 두 개 배울 예정
// useState : state 객체를 함수 컴포넌트에서 사용할 수 있도록 하는 기능. 
// useEffect : useState()와 함께 가장 많이 사용되는 hooks. react 생명주기 함수인 componenDidMount, 
// componentDidUpdate ,componentWillUnmount의 기능을 하나로 합한 기능
// useMemo : 재렌더링 시 연산량이 높은 작업을 반복하는 것을 피할 수 있게 하는 기능
// useCallback : useMemo와 비슷한 기능, 값이 아닌 함수를 반환함.
// useRef : 지정한 컴포넌트에 접근 할 수 있는 객체

// 훅을 사용 시 미리 해당 함수를 import해서 사용해야 함
// useState : class 컴포넌트에 존재하는 state 객체를 함수 컴포넌트에서 사용할 수 있도록 함.
// 사용법 :
// const [변수명, set함수명] = useState(초기값);
// 변수명 : state로 지정할 지역변수명
// set함수명 : state로 지정된 변수명에 접두사 set을 붙여서 사용, 카멜명명법 사용해야 한다.
// useState(초기값) : 확장표현식을 통해서 지정한 변수를 state 객체로 설정함, 초기값은 state로 설정된 변수의
// 초기값을 말한다.
// state로 지정할 변수가 여러 개일 경우, useState ()를 변수 수 만큼 실행해야 된다.


// useEffect : 클래스 컴포넌트에 존재하는 생명주기 함수를 사용할 수 있도록 한다.
// 사용법 :
// useEffect(이펙트함수, 의존성 배열); //이펙트함수 = 콜백함수
// 의존성 배열 : 콜백함수인 이펙트 함수가 의존하고 있는 배열, 해당 의존성 배열 안에 있는 변수 중 하나라도
// 값이 변경되면 실행이 된다.
// 이펙트 함수는 기본적으로 컴포넌트가 처음 렌더링된 후 데이터 업데이트에 의한 재렌더링 시 실행
// 이펙트 함수를 마운트, 언마운트 시 각각 1 번만 실행하고자 할 경우 의존성 배열에 빈배열을 사용(componentDidMount, componentWillUnmount 기능 수행)
// 의존성 배열이 없을 경우, 화면이 재랜더링 된 이후에 실행(componentDidUpdate 기능 수행)

// 훅의 규칙 :
// 1. 훅은 무조건 최상의 레벨에서만 호출
//  if, for문 내부에서 훅을 호출하면 안됨
// 2. 훅은 컴포넌트가 렌더링 될 때 마다 매번 같은 순서로 호출되어야 함
// 3. 함수 컴포넌트에서만 훅을 사용할 수 있음
//    일반적인 자바스크립트 함수에서 훅을 호출하면 안됨



// 함수 컴포넌트 사용
function CountButton2(props){
    // 지역변수
    // let count = 0;

    //useState를 통해서 state객체에 추가함
    const [count, setCount] = useState(0); //setCount를 통해서만 수정가능하게. 초기값은 0으로 지정.

    // 화면이 리로딩 될 때마다 업데이트된다
    // useEffect를 사용하여 componentDidMount, componentDidUpdate를 구현함
    useEffect(()=>{
        document.title = `${count}회 클릭했습니다.`
    }, [count]); //deps : [count] 카운트가 변경할 때만 동작 

    // 함수 선언
    const countUp = () => {
        // count++; 여기서 아래처럼 바뀌었다.

        setCount(count+1);
        console.log(`count : ${count}`);
    }

    const countDown = () => {
        // count--;

        // setCount(count-1);

        //이렇게 콜백함수 형태로 사용해도 된다.
        setCount((count)=>{
            return count-1;
        });
        console.log(`count : ${count}`); 
    }

    return (
        <div>
            <label className = {"form-label"}>count : {count}</label><br />
            <button className={"btn btn-primary"} onClick={countUp}> + </button>
            <button className={"btn btn-success"} onClick={countDown}> - </button>
        </div>
    )
}
export default CountButton2;

두 번째 예제 - 10명까지만, 0이하는 불가

UseCounter.jsx,Accommodate.jsx 생성


UseCounter.jsx

// UseCounter.jsx
// 훅 두 번 째 예제
import React,{useState} from "react";

// 커스텀 훅
// 이름에 use를 접두사로 사용
// 매개변수, 반환값을 사용자 마음대로 설정
// 내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함


// 커스텀 훅으로 설정
// 매개변수를 마음대로 설정함
function useCounter(initValue){ //porps대신 initValue 사용한 이유는 커스텀 훅이니까
    
    // state 사용을 위해서 useState 설정 함 initValue이 자리에 초기값 넣어줌
    const [count, setCount] = useState(initValue);

    // 함수 실행 시 state를 수정하기 위한 setCount를 실행 아래가 원본인데 축약 가능. setCount되면 화면이 재랜더링된다
    const increaseCount = () => {
        setCount((count)=> {
            return count + 1
        });
    }

    const decreaseCount = () =>{
        // 자바스크립트에서 지원하는 수학 클랫의 max 함수를 사용하여 0이하의 값을 사용할 수 없도록 제약 //    Math.max : 최댓값
        setCount((count)=> Math.max(count-1,0)); //0밑으로 안내려가게. 0이하로 못 내려간다

    //     이렇게 해도 된다. 위의 것은 더 심플하다.
    //     setCount((count)=>{
    //         count--;
    //         if (count < 0) {
    //             count = 0;
    //         }
    //         return count;
    //     })

    }
    return [count, increaseCount, decreaseCount];
}

export default useCounter;

Accommodate.jsx

// Accommodate.jsx

import React,{useState, useEffect} from "react";
import useCounter from "./UseCounter";

const MAX_CAPACITY = 10; //최댓값

function Accommodate(props){
    // state를 사용하기 위해 useState()를 설정
    const [isFull, setIsFull] = useState(false); //기본값은 false, 수정할려면 setIsFull을 사용해야한다
    // 해당 컴포넌트 내부에서 생성해야 할 state 객체 및 setState()를 커스텀 훅을 통해서 생성함
    // 커스텀 훅을 사용했기 때문에 재활용까지 가능함.
    const [count, increaseCount, decreaseCount] = useCounter(0);

    //원래는 아래처럼 직접 다 만들어야 했어야 했음
    // const [count ,setCont] = useState(0);
    // const increaseCount=( )=> {
    //
    // }

    // 커스텀 훅

// 리액트에서 사용하는 훅이 아닌 사용자가 필요에 의해서 생성하여 사용하는 훅
// 이름에 접두사로 use를 사용하고 함수 내부에서 다른 훅을 호출하는 단순 자바스크립트 함수
// 파라미터 및 반환값을 사용자가 직접 지정할 수 있음
// 중복되는 로직을 커스텀 훅으로 설정하여 재사용을 하기 위함
// 이름의 접두사로 use를 사용하지 않을 경우 함수 내부에서 훅을 사용하는지 판단할 수 없음


    // 리액트 생명주기 함수를 사용하기 위해서 useEffect를 설정함.
    // 의존성 배열이 없을 경우 componentDidMount, componentWillUnmount를 실행하는 것과 같은 효과
    useEffect(()=>{
        console.log("============");
        console.log("useEffect() is called");
        console.log(`isFull : ${isFull}`);
    },[]);
    
    // useEffect 두 번째 사용되고 있음. 반복가능
    // 의존성 배열에 count를 설정하여 count값이 수정되면 componentDidUpdate를 실행하는 것과 같은 효과
    useEffect(() => {
        setIsFull(count >= MAX_CAPACITY);
        console.log("현재 count 값 : " + count);
    }, [count]);
    
    return(
        <div>
            {/*현재 state로 설정된 count의 값을 출력*/}
            <p>{`${count}명 수용했습니다.`}</p>
            {/*커스텀 훅을 통해 만들어진 사용자 입장 / 퇴장 함수를 버튼에 등록 */}
            <button onClick={increaseCount} disabled={isFull} className={"btn btn-success"}>입장</button>
            <button onClick={decreaseCount} className = {"btn btn-primary" }>퇴장</button>
            {/* if문을 사용하지 못하기 때문에 삼항연산자 등을 사용해야한다.*/}
            {/* &&일 때 앞의 값이 false라면 뒤의 것 자체 실행하지 않는다. 랜더링 자체가 되지않음 ->console창에 아무것도 뜨지 않음
            true,true 여야지만 안의 내용이 출력된다*/}
            {isFull && <p style={{color :"red"}}>정원이 가득찼습니다</p>}
        </div>
    )
}

export default Accommodate;



문제 1) 숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를
출력하는 리액트 컴포넌트를 작성하세요

  • input 태그를 통해서 숫자를 입력 (num1, num2, result)
  • 버튼은 5개 (+, -, *, /, =) 생성
  • userState 사용 -> 함수 컴포넌트로 작성. 3개 설정해야한다
    (num1, num2, result 각 각 하나씩)
  • Calculator.jsx

Calculator.jsx

// Calculator.jsx
import React, {useState} from "react";

function Calculator(props){

    // const [result, setResult] = useState(0);
    // const
    // const resultPlus = ()=>{
    //     setResult()
    // }
    
    // 계산된 데이터를 저장하고 있을 변수(선생님 버전)
    let calResult = 0; //바깥에 있어야함

    const[num1, setNum1] = useState(0);
    const[num2, setNum2] = useState(0);
    const [result, setResult] = useState(0);
    
    // '='기호 사용해보기
    const [finalResult, setFinalResult] = useState(0);
    
    // const resultCal = () =>{}

    const plus = () => {
        setFinalResult(parseInt(num1) + parseInt(num2));
        console.log(`덧셈 결과  :  ${finalResult}`);

    //    const number1 = parseInt(num1);
    //    const number2 = parseInt(num2);
    //    calResult = number1 + number2;
    }

    const minus = () => {
        setFinalResult(num1 - num2);
        console.log(`뺄셈 결과  :  ${finalResult}`)
    }

    const multiply = () => {
        setFinalResult(num1 * num2);
        console.log(`곱셈 결과  :  ${finalResult}`)
    }

    const divisioin = () => {
        setFinalResult(num1 / num2);
        console.log(`나눗셈 결과  :  ${finalResult}`)
    }

    const equal = () => {
        setResult(finalResult);
    }


    return(
        <div>
            <br />
            <label className = {"form-label"} >숫자 2개를 입력하고 연산 버튼을 눌러주세요</label><br />
            <input className={"text m-3"} id={"num1"} name={"num1"} onChange={(e)=>setNum1(e.target.value)}/>
            <input className={"text"} id = {"num2" } name={"num2"} onChange={(e)=>setNum2(e.target.value)}/><br />
            <button className={"btn btn-primary m-3"} onClick={plus} > + </button>
            <button className={"btn btn-success m-3"} onClick={minus}>  - </button>
            <button className={"btn btn-success m-3"} onClick={multiply}> * </button>
            <button className={"btn btn-success m-3"} onClick={divisioin}> / </button>
            <button className={"btn btn-success m-3"} onClick={equal}> = </button><br />
            <label className = {"form-label" }>결과값</label><br />
            <input className = {"text" } id={"result"} value = {result}></input>
        </div>
    )
}
export default Calculator;



이벤트





이벤트 관련된 엄청 많은 정보를 알려준다.

Events.jsx

// Events.jsx

// 리액트는 html과 같은 이벤트를 가지고 있다.
// 카멜명명법을 사용하므로 onclick = "sum()" 에서 onClick={sum}으로 변경하여 사용한다.
// 매개변수 전달 시 이벤트 부분에 화살표 콜백 함수를 사용한다.
//      onClick = {() => sum(10)} 이런 형태로 사용된다
// 이벤트 사용 시 이벤트 헨들러도 매개변수로 전달이 가능하다.
//      onClick = {(event) => sum(10, event)}

import React from "react";


    // 기본 함수를 클릭이벤트와 연동
    const click1 = () => alert("일반 클릭 이벤트");
    
    // 매개변수가 있는 함수를 클릭이벤트와 연동
    const click2 = (item) => alert(`매개변수 값 : ${item}, \n매개변수가 있는 이벤트`)

    // 매개변수로 이벤트 핸들러(=이벤트 객체)를 사용하는 함수를 클릭이벤트와 연동
    // 이벤트 객체는 어느 객체에서 시작하는지 확인 가능하다.
    // 어떤 키로 작동되었는지 (ex. 키보드의 어느 키, 마우스 클릭 등) 확인 가능하다.
    const click3 = (item, event) => { //event : 자바스크립트가 원래 가지고 있는 이벤트 객체
        let msg = `매개변수와 event 객체가 있는 클릭 이벤트
        매개변수 값 : ${item}, 이벤트 객체 : ${event.type}`;
        console.log(event);

        alert(msg);
    }

function Events(){

    return(
        <div>
            <button type={"button"} className={"btn btn-primary"} onClick={click1}>일반 클릭 이벤트</button> 
            {/*함수이름쓰기 + 매개변수 적기*/}

            <button type={"button"} className={"btn btn-success"} onClick = {() => click2(100)}>매개변수가 있는 클릭 이벤트</button>

            {/*콜백함수에 event넣어준다. -> 내 버튼에서 발생한 이벤트 객체. 그걸 매개변수로 넘겨주는 것.*/}
            <button type={"button"} className={"btn btn-info"} onClick = {(event)=>click3(200,event)}>event 객체가 있는 클릭 이벤트</button>
        </div>
    )
}



export default Events;


Confirm


클릭하면 아래 처럼 확인 완료로 바뀐다

시간 설정한 만큼 뒤에 다시 돌아온다

ConfirmButton.jsx

// ConfirmButton.jsx

import React,{useState} from "react";

function ConfirmButton(props){
    const [isConfirmed, setIsConfirmed] = useState(false);
    // setIsConfirmed -> state 수정

    const handleConfirm = () => {
        //isConfirmed 콜백 함수 동작. 현재값 false에 not을 붙으면 true로 값이 들어감
        setIsConfirmed((isConfirmed) => !isConfirmed);
        setTimeout(()=>{
            setIsConfirmed(false);
        },2000); // 2초 후에 돌아온다
    };

    return(
        <div>
            {/*값이 true이면 disabled가 동작됨 => 버튼 사용할 수 없게 만듦*/}
            <button type={"button"} className={"btn btn-primary"} onClick={handleConfirm} disabled={isConfirmed}>
                {isConfirmed ? "확인 완료" : "확인하기"}
            </button>
        </div>
    )
}

export default ConfirmButton;
























0개의 댓글