티스토리 뷰

반응형

 이 포스팅은 https://nomadcoders.co/react-for-beginners 강의를 보며 작성하였습니다.

 

#3 State

 

이번에는 React에서 변수의 동적 제어를 하기 위해 State에 대해 공부하였다.

기존의 코드에서 counter의 변수를 증가시켜 준 후에 rerender을 통해서 페이지의 바뀐 부분을 변경해 주었다. 하지만, 변수와 함수가 많아지면 관리해야 하는 코드의 양도 늘어나고 실수도 늘어날 수 있다. 따라서 use.State함수를 통해 자동으로 state의 값이 변하면 rerender 해줄 수 있다.

const [vlaue, setValue] = React.useState("");

 위와 같이 React.useState() 함수를 이용하여 state 선언을 할 수 있다. 함수의 첫 번째 값은 state의 값이, 두 번째 값은 state값의 변경과 동시에 rerender 하는 함수(modifier function)를 받을 수 있다. 또한 useState()의 괄호 안에 인자값을 넣어 default값을 설정해 줄수도 있다.

<!DOCTYPE html>
<html>
<body>
    <div id="root"></div>
</body>
<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.getElementById("root");
    function App() {
        const [counter, setCounter] = React.useState(0);
        const onClick = ()=>{
            setCounter(counter + 1);
        }
        return (
        <div>
            <h3>Total clicks: {counter}</h3>
            <button onClick={onClick}> Click me </button>
        </div>);
    } 
    ReactDOM.render(<App/>, root);
</script>
</html>

 const [counter, setCounter] = React.useState(0); 구문의 첫 번째 값으로 counter을 받고 onClick 이벤트가 발생 시 setCounter함수를 이용하여 (counter +1)을 해주었다. 하지만, set함수를 이용할 때에는 이렇게 값의 변화를 직접적으로 계산하는 것보다 함수를 넘겨주는 것이 더 원하는 값을 얻을 수 있다고 한다. 즉 setCounter(counter+1)보다는 setCounter((current)=> curent+1)의 형태가 훨씬 더 안정적인 값을 얻을 수 있다고 한다. 

  ※공식 문서에서도 비동기 처리 때문에 함수로 전달하는 것을 권장하고 있습니다.

setState 호출은 비동기적으로 이뤄집니다. 따라서 setState 호출 직후 새로운 값이 this.state에 
반영될 거라고 믿어서는 안 됩니다. 이전 state 값을 기준으로 값을 계산해야 한다면 객체 대신 
updater 함수를 전달하세요.

 이를 바탕으로 값이 변할 때마다 직접적인 rerender을 하지 않아도 state값이 변하면 알아서 rerender을 해주는 useState()를 이용해서 간단한 예제인 분->시 변환기를 만들어 보고자 한다.

 

<!DOCTYPE html>
<html>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">

    function App() {
        const [minutes, setMinutes] = React.useState();
        const onChange = (event) => {
            setMinutes(event.target.value);
        }
        return (
        <div>
            <h1>Super Converter</h1>
            <label htmlFor="minutes">Minutes</label>
            <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}/>
            <h4>You want to convert {minutes}</h4>
            <label htmlFor="hours">Hours</label>
            <input id="hours" placeholder="Hours" type="number"/>
        </div>);
    } 
    const root = document.getElementById("root");
    ReactDOM.render(<App/>, root);
</script>
</html>

React의 스크립트 부분을 production -> development로 변경해 주었다.

<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>

 

minutes의 값을 state로 생성해 준다.

line12:

const [minutes, setMinutes] = React.useState();

 

변경감지 이벤트인 onChange에 onChange 함수를 할당해 준다. 그리고 onChange 함수는 event값을 통해 input값을 추적하고, 추적된 input값을 setMinutes() 함수의 인자값으로 전달하여 변경값을 넣어준 후 리랜더링을 통해 변경된 minutes값을 보여 줄 수 있도록 한다.

line13:

const onChange = (event) => {
            setMinutes(event.target.value);
      }

 이렇게 input의 데이터를 state로 연결해 어디에서든 input의 value를 수정할 수 있다.

<!DOCTYPE html>
<html>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">

    function App() {
        const [amount, setAmount] = React.useState("");
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event) => {
            setAmount(event.target.value);
        }
        const reset = () => {
            setAmount("");
        }
        const onFlip = () => {
            reset();
            setFlipped((current)=> !current);
        }
        return (
        <div>
            <h1>Super Converter</h1>
            <div>
                <label htmlFor="minutes">Minutes</label>
                <input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped}/>
            </div>
            <div>
                <label htmlFor="hours">Hours</label>
                <input value={flipped ? amount : Math.round(amount/60 * 10) / 10} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!flipped}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>{flipped ? "turn back" : "invert"}</button>
        </div>);
    } 
    const root = document.getElementById("root");
    ReactDOM.render(<App/>, root);
</script>
</html>

 여기에 flipped라는 조건 state를 하나 더 생성해 준다. 기본값은 false이다. 이 flipped의 역할은 조건에 따라 minutes와 hours의 비활성화 및 input값을 조정해 줄 것이다. flipped의 함수의 경우 현재값의 반대 값을 넣어주어 스위칭될 수 있도록 해준다. 따라서 flipped의 버튼을 클릭하면 false -> true로 변경될 것이다. flipped이 false일 경우는 minuets이 활성화가 될 것이고 반대로 true일 경우 hours가 활성화된다. 삼항연산자를 이용해서 입력된 값과 버튼의 메시지도 변경을 해주었다.

 reset 함수를 추가해 기본값으로 초기화시켜줄 함수도 추가했다.

 

<!DOCTYPE html>
<html>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
    function MinutesToHours() {
        const [amount, setAmount] = React.useState("");
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event) => {
            setAmount(event.target.value);
        }
        const reset = () => {
            setAmount("");
        }
        const onFlip = () => {
            reset();
            setFlipped((current)=> !current);
        }
        return (
        <div>
            <div>
                <label htmlFor="minutes">Minutes</label>
                <input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped}/>
            </div>
            <div>
                <label htmlFor="hours">Hours</label>
                <input value={flipped ? amount : Math.round(amount/60 * 10) / 10} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!flipped}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>{flipped ? "turn back" : "invert"}</button>
        </div>);
    } 
    function KmToMiles(){
        return <h3>KM 2 M</h3>
    }
    function App() {
        const [index, setIndex] = React.useState("xx");
        const onSelect = (event)=> {
            setIndex(event.target.value);
        };
        return (
        <div>
            <h1>Super Converter</h1>
            <select value={index} onChange={onSelect}>
                <option value="xx">Select your units</option>
                <option value="0">Minutes & Hours</option>
                <option value="1">Km & Miles</option>
            </select>
            <hr />
            {index === "xx" ? "목록에 있는 변환기를 선택해 주세요" : null}
            {index === "0" ? <MinutesToHours /> : null}
            {index === "1" ? <KmToMiles /> : null}
        </div>);
    } 
    const root = document.getElementById("root");
    ReactDOM.render(<App/>, root);
</script>
</html>

 마지막 작업으로 <select> 태그를 이용하여 목록 리스트를 만들어 주었다. 위에 방법과 똑같이 onChange 이벤트로 현재 상태의 값을 체크하고 체크된 값을 value값으로 할당해 주었다. 그리고 일반적인 구문을 사용하면 text로 화면에 출력이 되지만 {} 중괄호 안에서는 JavaScript구문을 사용할 수 있었다. 따라서 index의 번호에 맞게 페이지를 설정해 주는 법이 가능하다.

 

 이제 비어있는 KmToMiles를 내 스스로 채워보는 것이 숙제로 남아있다. 지금까지 사용했던 state를 이해하고 복습하며 KmToMiles를 채워볼까 한다.

<div>
    <h3>Km To Miles</h3>
    <div>
        <label htmlFor="Km"> Km</label>
        <input id="Km" placeholder="Km" type="number" disabled={false}/>
    </div>
    <div>
        <label htmlFor="Miles"> Miles</label>
        <input id="Miles" placeholder="Miles" type="number" disabled={true}/>
    </div>
    <button>Reset</button>
    <button>invert</button>
</div>

 return 구문 안에 html 구성부터 해주었다. 두 개의 버튼과 input칸을 만들어 입력할 수 있게 구성하였다. 우선 기본적인 html의 뼈대를 잡아두고 진행을 하였다.

function KmToMiles(){
        const [amount, setAmount] = React.useState("");
        const onChange = (event)=> {
            setAmount(event.target.value)
        }
        const reset = ()=>{
            setAmount("");
        }
        return (
            <div>
            <h3>Km To Miles</h3>
            <div>
                <label htmlFor="Km"> Km</label>
                <input value={amount} id="Km" placeholder="Km" type="number" onChange={onChange} disabled={false}/>
            </div>
            <div>
                <label htmlFor="Miles"> Miles</label>
                <input value={Math.round((amount / 1.609)*100)/100} id="Miles" placeholder="Miles" type="number" disabled={true}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button>invert</button>
            </div>
        );
    }

 기본적인 구조를 완성하고 유동적인 변수(amount)를 state로 선언해 주었다. onChange함수로 변경사항을 체크하고 input값을 value값으로 설정하였다. reset함수로 초기화도 만들어 주었다.

    function KmToMiles(){
        const [amount, setAmount] = React.useState("");
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event)=> {
            setAmount(event.target.value)
        }
        const reset = ()=>{
            setAmount("");
        }
        const onFlip = ()=> {
            reset();
            setFlipped((current)=>!current);
        }
        return (
            <div>
            <h3>Km To Miles</h3>
            <div>
                <label htmlFor="Km"> Km</label>
                <input value={flipped ? Math.round((amount * 1.609)*100)/100 : amount} id="Km" placeholder="Km" type="number" onChange={onChange} disabled={flipped}/>
            </div>
            <div>
                <label htmlFor="Miles"> Miles</label>
                <input value={flipped ? amount : Math.round((amount / 1.609)*100)/100} id="Miles" placeholder="Miles" type="number" onChange={onChange} disabled={!flipped}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>invert</button>
            </div>
        );
    }

 조건 함수인 filp을 생성해 조건을 설정해 주었다. (true&false) flip의 조건에 따라 input의 활성/비활성을 설정하였고 표시할 수도 삼항연산자를 이용해서 flip의 상태에 따라 입력값 자체의 value와 거리가 계산된 value를 표시해 주었다.  이렇게 전체적인 복습이 끝났다.

 

<전체 코드>

<!DOCTYPE html>
<html>
<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
    function MinutesToHours() {
        const [amount, setAmount] = React.useState("");
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event) => {
            setAmount(event.target.value);
        }
        const reset = () => {
            setAmount("");
        }
        const onFlip = () => {
            reset();
            setFlipped((current)=> !current);
        }
        return (
        <div>
            <h3>Minutes To Hours</h3>
            <div>
                <label htmlFor="minutes">Minutes</label>
                <input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped}/>
            </div>
            <div>
                <label htmlFor="hours">Hours</label>
                <input value={flipped ? amount : Math.round(amount/60 * 10) / 10} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled={!flipped}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>{flipped ? "turn back" : "invert"}</button>
        </div>);
    } 
    function KmToMiles(){
        const [amount, setAmount] = React.useState("");
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event)=> {
            setAmount(event.target.value)
        }
        const reset = ()=>{
            setAmount("");
        }
        const onFlip = ()=> {
            reset();
            setFlipped((current)=>!current);
        }
        return (
            <div>
            <h3>Km To Miles</h3>
            <div>
                <label htmlFor="Km"> Km</label>
                <input value={flipped ? Math.round((amount * 1.609)*100)/100 : amount} id="Km" placeholder="Km" type="number" onChange={onChange} disabled={flipped}/>
            </div>
            <div>
                <label htmlFor="Miles"> Miles</label>
                <input value={flipped ? amount : Math.round((amount / 1.609)*100)/100} id="Miles" placeholder="Miles" type="number" onChange={onChange} disabled={!flipped}/>
            </div>
            <button onClick={reset}>Reset</button>
            <button onClick={onFlip}>invert</button>
            </div>
        );
    }
    function App() {
        const [index, setIndex] = React.useState("xx");
        const onSelect = (event)=> {
            setIndex(event.target.value);
        };
        return (
        <div>
            <h1>Super Converter</h1>
            <select value={index} onChange={onSelect}>
                <option value="xx">Select your units</option>
                <option value="0">Minutes & Hours</option>
                <option value="1">Km & Miles</option>
            </select>
            <hr />
            {index === "xx" ? "목록에 있는 변환기를 선택해 주세요" : null}
            {index === "0" ? <MinutesToHours /> : null}
            {index === "1" ? <KmToMiles /> : null}
        </div>);
    } 
    const root = document.getElementById("root");
    ReactDOM.render(<App/>, root);
</script>
</html>

 

git : https://github.com/leesulgi66/React_for_Beginners/commit/f56ad059b359ae88f754b11348a546732b321f86

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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 31
글 보관함