React 시작 - (2)

React 시작 - (2)

‘React JS 정리 - (2)’

const root = document.getElementById("root");
   const App = () => {
    const [counter, setCounter] = React.useState(0);
    const onClick = () => {
      setCounter(counter + 1); // counter를 counter + 1로 변경시켜줌
    };

    return(
      <div>
        <h3>Total clicks : {counter}</h3>
        <button onClick={onClick}>Click me</button>
      </div>
    )
  };
    ReactDOM.render(<App />, root);

해당 방식의 문제점 -> 이전 state를 이용하여 현재 state를 바꾸려고 하지만 결과가 예상과 다르게 나올 수 있다.

왜냐하면 counter가 다른 곳에서 update될 수 있기 때문이다.

State를 바꾸는 방법은 2가지가 있다

  1. setCounter를 사용하여 우리가 원하는 값을 넣어주는것.
  2. 현재 state를 이용해서 다음 state를 계산해 내는 것. (ex) setCounter(counter + 1); 이전 counter 값을 이용해서 현재 counter값을 계산

2번 방법을 안전하게 쓰는 방법은

setCounter((current) => current + 1);
// react가 이 current가 확실히 현재 값이라는 걸 보장하므로 안전하다. 따라서 우리가 정확히 원하는 값으로 계산할수 있게 해준다.

-> 위와 같은 함수를 이용하는게 안전하다. 예상치 못한 업데이트가 어디선가 일어났다고 해도, 그게 혼동을 주는걸 방지해준다.

수정하면 다음과 같이 된다.

const root = document.getElementById("root");
   const App = () => {
    const [counter, setCounter] = React.useState(0);
    const onClick = () => {
      setCounter((current) => current + 1); // 변경된 부분
    };

    return(
      <div>
        <h3>Total clicks : {counter}</h3>
        <button onClick={onClick}>Click me</button>
      </div>
    )
  };
    ReactDOM.render(<App />, root);

시간 변환 예제

JSX는 HTML과 유사하여 그냥 평범하게 HTML로만 쓸수도 있다.

 <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>

다음과 같이 jsx를 쓰고 개발자도구를 열어보면 문제가 없는거처럼 보이지만, react를 가져온 스크립트 에서 production.min 부분을 development로 수정해서 개발자도구를 확인해보면 에러가 뜨는 것을 볼 수 있다. label에 있는 for는 javascript에서 이미 선점한 용어이기 때문이다. 마찬가지로 class도 js에 이미 선점되어 있는 용어이다. jsx에서 class를 쓸때는 className=“” 으로 써야하고, for은 htmlFor=“”을 써야한다.

react에서 제어되어있지 않은 input은 uncontrolled로 알려저 있다. 해당 input의 value는 우리가 통제할 수 없다는 뜻이다.

   const root = document.getElementById("root");
   const App = () => {
    const [minutes, setMinutes] = React.useState();
    const onChange = (event) => {
      console.log(event);
    }
    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);

콘솔창에서 확인해보면 다음과 같은 콘솔창을 볼 수 있다.

react는 가짜 이벤트를 발생시킨다. 해당 가짜 event는 event를 최적화 시킨다.

원래의 event(Native Event)를 얻을수도 있다.

해당 이벤트를 콘솔창에서 뜯어보면 target 안에 value에 우리가 입력한 값이 저징되는 것을 알 수 있다. {이미지 추가}

   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}/>
        <h3>You want to convert {minutes}</h3>
        <label htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number"/>
      </div>
    )
  };

위 코드로 controlled input을 완성한다.

const root = document.getElementById("root");
   const App = () => {
    const [minutes, setMinutes] = React.useState(0);
    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}/>
        <h3>You want to convert {minutes}</h3>
        </div>
        <div>
        <label htmlFor="hours">Hours</label>
        <input value={minutes / 60} id="hours" placeholder="Hours" type="number"/>
        </div>
      </div>
    )
  };
    ReactDOM.render(<App />, root);

div를 추가해주고 hours 계산 부분을 추가. hours에는 onChange라는 이벤트가 적용되지 않았으므로 업데이트가 일어나지 않기 때문에 hour칸에 입력하여 변경하려고 해도 minutes으로 인해 발생한 onChnage 이벤트로 적용된 값 이외에는 갱신이 되지 않아 값이 그대로 있다. -> 입력되지 않는것과 같은 효과. 시간단위로 보여주고 싶으면 minutes / 60 부분을 Math.round(minutes / 60)으로 수정하면 된다.

<!DOCTYPE html>
<html lang="ko">
  <body>
    <div id="root"></div>
  </body>
  <script src="https://www.unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@18.3.1/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(0);
    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"/>
        </div>
        <button onClick={reset}>Reset</button>
      </div>
    )
  };
    ReactDOM.render(<App />, root);

  </script>
</html>

리셋 버튼까지 적용시킨 코드이다.

<!DOCTYPE html>
<html lang="ko">
  <body>
    <div id="root"></div>
  </body>
  <script src="https://www.unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@18.3.1/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(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>
        <h1>Super Converter</h1>
        <div>
        <label htmlFor="minutes">Minutes</label>
        <input value={inverted ? (amount * 60) : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={inverted === true}/>
        </div>
        <div>
        <label htmlFor="hours">Hours</label>
        <input value={inverted ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" disabled={inverted === false} onChange={onChange}/>
        </div>
        <button onClick={reset}>Reset</button>
        <button onClick={onInvert}>{inverted ? "Turn Back" : "Invert"}</button>
      </div>
    )
  };
    ReactDOM.render(<App />, root);

  </script>
</html>

invert 기능을 추가하여 invert 될때마다 minutes와 hours 입력모드가 바뀌는걸 적용 state를 하나 더 추가하여 invert 될때의 이벤트를 적용

inverted의 디폴트값을 false로 설정하고 invert버튼 누를때마다 onInvert함수가 실행되서 setInverted라는 modifier함수를 실행시켜서 inverted를 반대값으로 변경한다. false일 경우 true, true일 경우 false로 변경.

inverted값이 false일 경우 minutes input은 활성화, hours input은 비활성화 -> disable에 조건식을 줘서 설정

inverted값이 false일 경우 minutes input은 입력한 값 그대로, hours input에는 amount / 60인 값이 나오도록 한다. true일 경우 minutes input은 amount * 60, hours input에는 입력값 그대로 표시되게 한다. -> 삼항연산자를 사용하여 설정

invert 버튼에 삼항연산자를 적용하여 버튼에 출력된 문구를 inverted의 값에 따라 변경