React: State 연습

summereuna🐥·2021년 11월 17일
0

React JS

목록 보기
5/69

참고
react, reactdom을 import하는 script tag에서
production은 배포 모드, development는 개발 모드를 의미한다.
개발모드는 버그로 이어질 수 있는 요소들을 미리 경고하는 검증 코드가 포함되어 있다.

Unit Converter APP (단위 변환 앱) 만들기

minutes <-> hours

1. input 만들어 플레이스홀더에 시/분 적기

<!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");
    const App = () => {
      return (
        <div>
          <h1>Super Converter</h1>
          <label for="minutes">Minutes</label>
          <input id="minutes" placeholder="Minutes" type="number" />
          <label for="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

이렇게 써주면 label이 input과 연결되어서 label 글씨를 클릭하면 input 입력 창에 focus가 된다.
그런데!!! 여기서 주의할 점은, 지금 JSX을 사용하고 있지 html을 사용하는 것이 아니란 점이다!! 위에 작성한 내용은 html이다.

JSX는 html과 비슷하지만 다른 점이 몇 가지 있다.

HTMLJSX
classclassName
forhtmlFor
<!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");
    const App = () => {
      return (
        <div>
          <h1>Super Converter</h1>
          <label htmlFor="minutes">Minutes</label>
          <input id="minutes" placeholder="Minutes" type="number" />
          <label htmlFor="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

2. hours 인풋에 시간 적으면 분 단위가 갱신되고, minutes 인풋에 입력하면 시 단위 갱신되게하기

2-1. 인풋에 적힌 value를 알아야 계산을 한다.

React JS에서 input은 uncontrolled 라고 알려져 있는데, input의 value를 통제할 수 없기 때문이다.

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

useState()는 array를 제공하는데, 그 첫 번재 element가 현재 값이다.
그렇기 때문에 현재 값의 이름을 minutes으로 주고, 그 값을 변경시키는 함수의 이름을 setMinutes으로 주자.

그러면 minutes 인풋의 value는 minutes이겠지.

    const root = document.getElementById("root");
    const App = () => {
      const [minutes, setMinutes] = React.useState(); // 👈 state에 있는 minutes값
      return (
        <div>
          <h1>Super Converter</h1>
          <label htmlFor="minutes">Minutes</label>
          <input // 👈 minute값을 넣어주는 input
            value={minutes} // 🔥 그 값은 minutes이고 state에 있다.
            id="minutes"
            placeholder="Minutes"
            type="number"
          />
          <label htmlFor="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
        </div>
      );
    };
    ReactDOM.render(<App />, root);

2-2. 사용자가 다른 값을 입력할 때 마다 minute input의 value 업데이트하기

input에 변화가 생길때 마다 그 변화(사용자가 input에 뭔가 입력하는 것)를 리스닝하기 위해서는 onChange event를 리스닝 하면 된다.

  1. onChange 이벤트
<label htmlFor="minutes">Minutes</label>
          <input // 👈 minute값을 넣어주는 input
            value={minutes} // 🔥 그 값은 minutes이고 state에 있다.
            id="minutes"
            placeholder="Minutes"
            type="number"
            onChange={onChange}
          />
  1. state로써 minutes 값을 input의 value로 넣어주면, input의 value가 state의 value와 같아진다.

react JS에서 form을 다루는 방법

JS랑 비슷해서 input에 입력한 값을 가져오기 위해서 체인지 이벤트를 사용하여 이벤트 타겟 벨류 값을 가져온다.

const root = document.getElementById("root");
    const App = () => {
      const [minutes, setMinutes] = React.useState();
      const onChange = (event) => {
        console.log(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}
          />
          <label htmlFor="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
        </div>
      );
    };
    ReactDOM.render(<App />, root);

사용자가 실시간으로 입력하는 값인 event.target.value를 minutes state에 넣어주기 위해 setMinutes에 이벤트 타겟 밸류를 보내주면된다.

  • 잘 작동하는지 확인하기 위해 변수를 보내서 테스트 해보자.
<!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");
    const 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}
          />
          <label htmlFor="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
          <h4>You want to convert {minutes}</h4>
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

  • hours input에도 동일한 value 값을 주고 인풋 별로 div로 묶어 정리해 준다.
<!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");
    const App = () => {
      const [minutes, setMinutes] = React.useState();
      const onChange = (event) => {
        setMinutes(event.target.value);
      };
      return (
        <div>
          <h1>Super Converter</h1>
          <div>
            <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={minutes} id="hours" placeholder="Hours" type="number" />
          </div>
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

minutes 인풋으로 부터 받은 밸류값을 공유하기 때문에 hours 인풋에도 동일한 값이 뜬다. 하지만 hours input은 수정을 못하는데 아직 onChange event 설정을 하지 않아서 그렇다.

minutes/60 이면 hours이기 때문에 hours input의 밸류 값을 수정해 준다.
그리고 hours input에 disabled 속성을 주자..ㅇㅇ..! 일단

<!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");
    const App = () => {
      const [minutes, setMinutes] = React.useState();
      const onChange = (event) => {
        setMinutes(event.target.value);
      };
      return (
        <div>
          <h1>Super Converter</h1>
          <div>
            <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={minutes / 60} //👈
              id="hours"
              placeholder="Hours"
              type="number"
              disabled
            />
          </div>
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

reset button 만들기

reset 버튼을 클릭하면 setMinutes(0);이 작동되게 하여 state를 리스닝하거나 연결된 모든 것들은 전부 0으로 리셋된다.

<!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");
    const App = () => {
      const [minutes, setMinutes] = React.useState();
      const onChange = (event) => {
        setMinutes(event.target.value);
      };
      const reset = () => setMinutes(0); //👈
      return (
        <div>
          <h1>Super Converter</h1>
          <div>
            <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={minutes / 60}
              id="hours"
              placeholder="Hours"
              type="number"
              disabled
            />
          </div>
          <button onClick={reset}>Reset</button> //👈
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

hours -> minutes 로도 단위로 변환하기

#3.7 State Practice part Two

flip 버튼을 누르면 반대로 minutes을 쓸 수 없게(disabled)되고 hours에 값 입력할 수 있게 하기.

disabled 값은 true값을 명시적으로 쓸수도 있다.

state값으로 input을 enabled/disabled할지 결정하면 된다.

  1. 디폴트 현재 상태는 flipped가 false인 상태이다
  • 이 상태에서는 disabled={flipped === flase}로 설정되어 있는 hours인풋은, disabled={true}이기 때문에 입력을 못한다.
  • disabled={flipped === true}로 설정되어 있는 minutes인풋은, disabled={false}이기 때문에 입력을 할 수 있다.
  1. 그런데 flip 버튼을 누르면, setFlipped가 현재 상태(current)의 반대 상태(!current)를 만들어 준다.
  • 이 상태에서는 disabled={flipped === flase}로 설정되어 있는 hours인풋은, disabled={true}가 되기 때문에 입력할 수 있게 된다.
  • disabled={flipped === true}로 설정되어 있는 minutes인풋은, disabled={false}가 되기 때문에 입력을 못하게 된다.
    그렇기 때문에 flipped이 false 인 상태에서 true인 상태가 되는 거다.
    이렇게 되면
<!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");
    const App = () => {
      const [minutes, setMinutes] = React.useState();
      //새로운 state 조작
      //useState의 default 값 flase로 설정
      const [flipped, setFlipped] = React.useState(false);
      const onChange = (event) => {
        setMinutes(event.target.value);
      };
      const reset = () => setMinutes(0);
      const onFlip = () => setFlipped((current) => !current);
      //flipped가 false 상태면, true 반환
      //flipped가 true 상태면, false 반환
      //setFlipped에 현재 값(flipped의 정반대 값)을 입력
      //현재 state를 바탕으로 새로운 state 계산
      return (
        <div>
          <h1>Super Converter</h1>
          <div>
            <label htmlFor="minutes">Minutes</label>
            <input
              value={minutes}
              id="minutes"
              placeholder="Minutes"
              type="number"
              onChange={onChange}
              //플립이 true라면 disabled이 true가 되는 조건 (인데 일반적인 JS코드임)
              //disabled={flipped === true} 혹은
              disabled={flipped}
            />
          </div>
          <div>
            <label htmlFor="hours">Hours</label>
            <input
              value={minutes / 60}
              id="hours"
              placeholder="Hours"
              type="number"
              //플립이 false라면 disabled이 true가 되는 조건
              //disabled={flipped === false} 혹은
              disabled={!flipped}
            />
          </div>
          <button onClick={reset}>Reset</button>
          <button onClick={onFlip}>Flip</button>
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

Hours input의 chnage event를 리스닝하기

  1. hours input의 props에 onChange={onChange}를 추가해주면 이제 숫자를 써 넣을 수는 있게 된다.

  2. 문제가 있다. value가 value={minutes / 60} 이런식으로 계산이 된다. 이 변환 공식은 Minutes input에 적었을 때만 일어나야 한다. hours input에는 변환 없이 그냥 입력한 숫자를 보여주려면 어떻게 해야 할까?
    value 부분을 삼항연산자(if문 인라인 형태로 적기)로 적어주자.
    (+)플립했을 때 reset()되게 리셋 함수도 추가

<!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");
    const App = () => {
      const [amount, setAmount] = React.useState();
      const [flipped, setFlipped] = React.useState(false);

      const onChange = (event) => {
        setAmount(event.target.value);
      };
      const reset = () => setAmount(0);
      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 : amount / 60}
              id="hours"
              placeholder="Hours"
              type="number"
              disabled={!flipped}
              onChange={onChange}
            />
          </div>

          <button onClick={reset}>Reset</button>
          <button onClick={onFlip}>Flip</button>
        </div>
      );
    };
    ReactDOM.render(<App />, root);
  </script>
</html>

km <-> m

kg <-> p

profile
Always have hope🍀 & constant passion🔥

0개의 댓글