FrontEnd/React

nomadcorders_ReactJS로 영화 웹 서비스 만들기_DAY2

Leo.K 2023. 3. 24. 16:14

2023-03-24
#3 State

state : 기본적으로 데이터를 저장하는 저장소. 바뀔 변수의 값을 저장해 줄 저장소.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
    
    const root = document.querySelector("#root");
    let counter = 0;
    function countUp (){
        counter += 1;
        render(); //변경된 값이 적용되도록 Container를 리렌더링 해줘야 한다.
    }
    function render() {
        ReactDOM.render(<Container />, root);
    }
    const Container = () =>
        <div>
            <h3>Total clicks : {counter}</h3>
            <button onClick={countUp}>Click me</button>
        </div>
    render();
</script>
</body>
</html>

이번 강의에서는 바뀐 값을 저장해줄 state 변수에 대해서 알아보았다. 

0. 문제점
Container라는 컴포넌트를 만들면서 내부의 버튼에 이벤트를 등록해주고 count를 증가시키는 것은 어렵지 않다. 
하지만 문제점은 가장 초기의 상태(counter == 0)를 렌더링 해주고, 그 이후에는 렌더링을 하지 않기 때문에 내부적으로 클릭이벤트가 발생할 때마다 counter의 값은 1씩 증가하는데, 그 값이 보이지 않는다는 점이다.
단순하게 클릭 이벤트가 발생하여 값이 증가할때마다 reRendering하여 값을 갱신해주는 방법이 있다.

1. vanilla JS VS react JS
여기서 바닐라 JS와 React Js의 가장 큰 차이점이 드러난다. 
먼저 바닐라 JS를 적용한 브라우저는 노드 정보가 바뀔때(값이 업데이트 될 때)마다 전체 노드 트리를 처음부터 다시 생성한다. 간단한 구조라면 큰 차이는 없겠지만, 실무에서와 같은 큰 프로젝트가 된다면 말로만 들어도 비효율적인 작업이 많이 발생할 것이다.
하지만 리액트 JS는 가상 DOM구조를 사용한다는 점이다.
리액트는 이전에 전달받은 컴포넌트 정보를 기억하고 있다가 새로운 컴포넌트가 렌더링 되면 이전에 가지고 있던 컴포넌트 정보와 비교하여 변경된 부분을 감지한다.
가상 DOM을 사용하여 이미 렌더링 된 부분에서 변경된 부분만 업데이트하고, 모든 업데이트가 종료되면 일괄적으로 합쳐서 실제 DOM에 던져주는 방식을 사용한다. 

여기서 끝이 아니라 이제는 reRendering을 좀 더 효율적으로 호출할 수 있는 방법을 알아보자.

 

2023.03.27
#3.1 setState part One

지난 시간에 변경된 값을 페이지에 리렌더링 하는 방법론에 대해서 고민을 해보았는데 오늘 그 방법을 소개해볼까 한다.
React에서 지원하는 state를 사용하면 된다. useState를 사용하면 크기가 2인 배열을 준다.

const data = React.useState(0); 
console.log(data); //--> (2) [0, f] == [initial data, modifier for data]
// useState메서드를 사용할 때 초깃값 0을 잡아주고 console.log를 찍어보자. 
// 초기화한 데이터와 이 데이터를 수정할 수 있는 함수가 제공된다.

//배열의 인덱스 대신 이름 붙이기 (같은 표현 방식이다.)
// --기존
const data = [1, 2];
data[0] = 1;
data[1] = 2;

const one = data[0];
const two = data[1]; 

one -> 1
two -> 2;

// --압축 문법
const [one, two] = data;

 

#3.2 setState part Two
useState로 부터 전달받은 setCounter함수를 통해 counter라는 데이터를 변경하여 Component를 변경된 data로 새로 생성한 후에 자동으로 reRendering된다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="root"></div>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
    const root = document.querySelector("#root");
    function App () {
        const [counter, setCounter] = React.useState(0); //(2) [initial data, ƒunction for change data]
        const onClick = () => {
            setCounter(counter+1); //state로부터 전달받은 modifier함수가 직접 reRendering을 해준다.
        };
        return (
            <div>
                <h3>Total clicks : {counter}</h3>
                <button onClick={onClick}>Click me</button>
            </div>
        );
    }
    ReactDOM.render(<App />, root);
</script>
</body>
</html>

 

2023.03.28
# 3.4 State Function 

현재 값을 기준으로 값(state)을 바꾸는 방법

setCounter(counter+1); //state로부터 전달받은 modifier함수가 직접 reRendering을 해준다.
setCounter((current) => current + 1);

//1의 방식은 counter값이 다른 곳에서도 사용할 수 있기 때문에 setCounter메서드가 실행되는 시점에 counter가
//현재 값이라는 보장이 없다. -> 예상과는 다른 결과가 나올 수 있다.

//2의 방식은 리액트가 현재값이라는 것을 보장해준다. -> 함수형으로 작성하자.

 

#3.5 Inputs and State

function App () {
    const [minutes, setMinutes] = React.useState(0);
    const [hours, setHours] = React.useState();
    const onChange = (event) => {
        setMinutes(event.target.value);
    }

    const reset = () => {
        setMinutes(0);
    }
    return (
        <div>
            <div>
                <h1 className="hi">Super Converter</h1>
                <label htmlFor="minutes">Minutes</label>
                <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} /> 
            </div>

            <div>
                <label htmlFor="hours">Hours</label>
                <input value={Math.round(minutes/60)} id="hours" placeholder="Hours" type="number" disabled="disabled"/>
            </div>
            <button onClick={reset}>Reset</button>
        </div>
    );
}
const root = document.querySelector("#root");
ReactDOM.render(<App />, root);

현재 자바스크립트가 아닌 react를 import 했기 때문에 class나 for같은 예약어는 사용할 수 없다.
대신하여 className, htmlFor를 사용하자.

input태그에 값이 입력되었을 때(값의 변경이 감지 되었을 때 -> onChange), 변경된 값을 reRendering해주고, 그 값을 시간으로 변환하여 렌더링 해준다.

2023.03.29
# State Practice

state값을 활용하여 input창을 enable <-> disable 하기

function App () {
    const [amount, setAmount] = React.useState(0);
    // const [hours, setHours] = React.useState();
    const [inverted, setInverted] = React.useState(false);
    const onChange = (event) => {
        setAmount(event.target.value);
    }

    const reset = () => {
        setAmount(0);
    }
    const onInvert = () => {
        reset();
        setInverted((current) => !current);
    };
    return (
        <div>
            <div>
                <h1 className="hi">Super Converter</h1>
                <label htmlFor="minutes">Minutes</label>
                <input
                    value={inverted ? amount * 60 : amount}
                    id="minutes"
                    placeholder="Minutes"
                    type="number"
                    onChange={onChange}
                    disabled={inverted}/>
            </div>

            <div>
                <label htmlFor="hours">Hours</label>
                <input
                    value={inverted ? amount : Math.round(amount/60)}
                    id="hours"
                    placeholder="Hours"
                    type="number"
                    onChange={onChange}
                    disabled={!inverted}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onInvert}>{inverted ? "Turn back" : "Invert"}</button>
        </div>
    );
}
const root = document.querySelector("#root");
ReactDOM.render(<App />, root);

 

2023.5.9
상황에 따라 다른 컴포넌트를 렌더링하기 

    function MinutesToHours () {
        const [amount, setAmount] = React.useState(0);
        const [inverted, setInverted] = React.useState(false);
        const onChange = (event) => {
            setAmount(event.target.value);
        }

        const reset = () => {
            setAmount(0);
        }
        const onInvert = () => {
            reset();
            setInverted((current) => !current);
        };
        return (
            <div>
                <div>
                    <label htmlFor="minutes">Minutes</label>
                    <input
                        value={inverted ? amount * 60 : amount}
                        id="minutes"
                        placeholder="Minutes"
                        type="number"
                        onChange={onChange}
                        disabled={inverted}/>
                </div>

                <div>
                    <label htmlFor="hours">Hours</label>
                    <input
                        value={inverted ? amount : Math.round(amount/60)}
                        id="hours"
                        placeholder="Hours"
                        type="number"
                        onChange={onChange}
                        disabled={!inverted}/>
                </div>
                <button onClick={reset}>Reset</button>
                <button onClick={onInvert}>{inverted ? "Turn back" : "Invert"}</button>
            </div>
        );
    }
    function KmToMiles(){
        const [amount, setAmount] = React.useState(0);
        const [inverted, setInverted] = React.useState(false);
        const onChange = (event) => {
            setAmount(event.target.value);
        }

        const reset = () => {
            setAmount(0);
        }
        const onInvert = () => {
            reset();
            setInverted((current) => !current);
        };
        return (
            <div>
                <div>
                    <label htmlFor="km">KiloMeter</label>
                    <input
                        value={inverted ? amount/1.609344 : amount}
                        id="km"
                        placeholder="KiloMeter"
                        type="number"
                        onChange={onChange}
                        disabled={inverted}/>
                </div>

                <div>
                    <label htmlFor="miles">Miles</label>
                    <input
                        value={inverted ? amount : amount*1.609344}
                        id="miles"
                        placeholder="Miles"
                        type="number"
                        onChange={onChange}
                        disabled={!inverted}/>
                </div>
                <button onClick={reset}>Reset</button>
                <button onClick={onInvert}>{inverted ? "Turn back" : "Invert"}</button>
            </div>
        )
    }
    function App () {
        const [index, setIndex] = React.useState("0")
        const onSelect = (event) => {
            setIndex(event.target.value);
        };

        return (
            <div>
                <h1 className="hi">Super Converter</h1>
                <hr/>
                <select value={index} onChange={onSelect}>
                    <option value="0">Minutes & Hours</option>
                    <option value="1">Km & Miles</option>
                </select>
                {index === "0" ? <MinutesToHours/> : null}
                {index === "1" ? <KmToMiles/> : null}
            </div>
        );
    }
    const root = document.querySelector("#root");
    ReactDOM.render(<App />, root);
  • 속성으로 value를 가지는 태그는 onChange 리스너를 사용할 수 있다. 
  • 드롭다운으로 선택한 값에 따라 다른 컴포넌트를 렌더링 할 수 있다. 
  • useState의 두 번째 인자인 modifier함수가 실행되면 해당 컴포넌트가 리렌더링 된다.
  • 리렌더링 조건
    • props이 바뀔 때
    • state가 바뀔때
    • 부모 컴포넌트가 리렌더링 될 때
      • MinutesToHours, KmToMiles의 부모 컴포넌트가 App인 셈이다.