참고
react, reactdom을 import하는 script tag에서
production은 배포 모드, development는 개발 모드를 의미한다.
개발모드는 버그로 이어질 수 있는 요소들을 미리 경고하는 검증 코드가 포함되어 있다.
<!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이다.
HTML JSX class className for htmlFor
<!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>
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);
input에 변화가 생길때 마다 그 변화(사용자가 input에 뭔가 입력하는 것)를 리스닝하기 위해서는 onChange event를 리스닝 하면 된다.
<label htmlFor="minutes">Minutes</label>
<input // 👈 minute값을 넣어주는 input
value={minutes} // 🔥 그 값은 minutes이고 state에 있다.
id="minutes"
placeholder="Minutes"
type="number"
onChange={onChange}
/>
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>
<!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 버튼을 클릭하면 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>
#3.7 State Practice part Two
flip 버튼을 누르면 반대로 minutes을 쓸 수 없게(disabled)되고 hours에 값 입력할 수 있게 하기.
disabled 값은 true값을 명시적으로 쓸수도 있다.
state값으로 input을 enabled/disabled할지 결정하면 된다.
disabled={flipped === flase}
로 설정되어 있는 hours인풋은, disabled={true}이기 때문에 입력을 못한다.disabled={flipped === true}
로 설정되어 있는 minutes인풋은, disabled={false}이기 때문에 입력을 할 수 있다.disabled={flipped === true}
로 설정되어 있는 minutes인풋은, disabled={false}가 되기 때문에 입력을 못하게 된다.<!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의 props에 onChange={onChange}
를 추가해주면 이제 숫자를 써 넣을 수는 있게 된다.
문제가 있다. 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>