티스토리 뷰
이 포스팅은 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
'개발일지 > React' 카테고리의 다른 글
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #7 Practice Movie App (0) | 2024.08.20 |
---|---|
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #6 Effects (0) | 2024.08.15 |
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #5 Create React App (0) | 2024.08.13 |
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #4 Props (0) | 2024.08.09 |
React를 배워보자(feat. 노마드 코더 - ReactJS 로 영화 웹 서비스 만들기) (0) | 2024.08.03 |