[React JS] #2. State (feat. Nomadcoders)

toto9602·2023년 11월 12일
0

내용 요약

  • React JS는 React DOM을 사용하여 UI가 변경되는 부분만을 효율적으로 업데이트할 수 있다.
  • 변경된 값을 자동으로 re-rendering하기 위해 useState에서 반환되는 modifier 함수를 사용한다.
  • modifier 함수의 인자가 되는 콜백 함수는, 인자로 event를 받을 수 있다.

State란? (Understanding State)

state : 데이터가 저장되는 곳

React JS로 counter를 적용하는 방법 #1

<script type="text/babel">
  const root = document.getElementById("root");
  let counter = 0;
  function countUp() {
  	counter = counter + 1;
 	render(); // 
  	}
  	function render() {
  		ReactDOM.render(<Container />, root);
  	}
  const Container = () => (
  	<div>
      <h3>Total clicks : {counter}</h3>
      <button onClick={countUp}>Click me</button>
  </div>
  );
  	render();
	</script>

→ 카운터가 증가할 때마다, 다시 렌더링해주기 위해 countUp 함수에서 render 함수를 재호출

→ 다만, render를 재호출하는 것을 작성자가 빠뜨릴 수 있으므로, 사실 좋은 방법이 아니다.

React의 렌더링

  • React Js는 UI에서 바뀌는 부분만 업데이트할 수 있음.

cf. 강의 댓글 중에서 (from. soonseok 님)

[ Vanilla Javascript의 노드 변경 처리 ]

  • Vanilla Javascript는 DOM 변경을 직접 처리한다.
    이는 DOM 요소의 선택, 속선 변경, 추가, 제거 등을 포함!

[ React JS ]

  • ReactJS는 DOM 변경 처리를 위해 가상 DOM(Virtual DOM)이라는 개념을 도입
  • 메모리에, 실제 DOM 트리의 사본인 가상 DOM 트리를 생성한다.
  • 상태 변경이 발생할 때마다, 새로운 가상 DOM 트리를 생성하고, 이전의 가상 DOM 트리와 비교하여 변경된 부분을 파악 후, 파악된 변경 사항만 실제 DOM에 반영하는 방식을 사용!
  • 이를 재조정(Reconciliation), 또는 Diffing이라고 한다.
  • 이를 통해 변경이 필요한 최소한의 요소만 실제 DOM에 반영하여 복잡한 UI 업데이트를 효과적으로 처리할 수 있다!

SetState

React JS로 counter를 적용하는 방법 #2

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

[ React.useState의 사용 ]

  • 담고자 하는 데이터의 초기값과, 이 데이터를 조작할 수 있는 함수를 반환한다.
  • modifier 함수는 값을 변경하고, 자체적으로 컴포넌트 리렌더링을 해 준다.

modifier 함수를 사용하여 state를 변경할 때, 컴포넌트가 재생성되며 리렌더링된다!

State Functions

기존 코드의 문제

  • counter + 1을 실행할 때, counter 변수가 다른 곳에서 변경되었을 수 있음.
  • 그에 따라 예상과 다른 결과가 발생할 우려가 있음.

현재 값으로 state 수정하기

&rarr -> 현재 값을 인자로 받는, 콜백 함수를 사용하기.

const onClick = () => {
setCounter(counter + 1);
setCounter((current => current + 1)
}

→ 확실히 현재의 값이라는 걸 보장할 수 있는 방법!

Inputs and State

시간 - 분 Converter 작성

1. Input 박스 추가하기

<script type="text/babel">
function App() {
	return (
		<div>
          <h1 className="hi">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>
)
}
const root = document.getElementById("root");
ReactDOM.render(
          }
</script>
  • label 태그에서 기본적인 forh1태그에서 기본적인 class를 사용하면,
    이는 JSX에서는 javascript 예약어이므로 문제가 발생할 수 있음.
  • for는 HtmlFor, classclassName으로 수정해 줘야 한다.

cf. script tag의 변경

  • 위의 에러는 React, ReactDOM을 import하는 script 태그를 production -> development로 변경해야 확인이 가능
  • production은 배포 모드, development는 개발 모드를 의미
  • 개발 모드는 버그로 이어질 수 있는 요소들을 미리 경고하는 검증 코드가 포함되어 있다.

2. 입력값 추적하기

<script type="text/babel">
function App() {
	const [minutes, setMinutes] = React.useState();
	const onChange = (event) => {
  		setMinutes(event.target.value);
	}
	return (
		<div>
          <h1 className="hi">Super Converter</h1>
          <label htmlFor="minutes">Minutes</label>
          <input id="minutes" placeholder="Minutes" type="number" onChange={onChange} />
          <label htmlFor="hours">Hours</label>
          <input id="hours" placeholder="Hours" type="number" />
</div>
)
}
const root = document.getElementById("root");
ReactDOM.render(
          }
</script>
  • input에 변화가 생기면 (onChange) event를 매개 변수로 받는 onChange 함수를 실행한다.
  • event에서 입력된 값을 꺼내서 setMinutes 함수로 설정해 준다.

[ cf. SyntheticEvent ]

  • ReactJS가 event를 최적화한 결과로, ReactJs가 발생시키는 이벤트!

3. 동일한 내용을 Hours input에 적용 && 시간 변환 공식 적용

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

4. Reset 버튼 추가

<script type="text/babel">
function App() {
	const [minutes, setMinutes] = React.useState();
	const onChange = (event) => {
  		setMinutes(event.target.value);
	}
  	const reset = () => setMinutes(0); // 추가한 부분!!
  
	return (
		<div>
          <h1 className="hi">Super Converter</h1>
          <div>
            <label htmlFor="minutes">Minutes</label>
          	<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} />
          	<label htmlFor="hours">Hours</label>
          	<input value={minutes / 60} id="hours" placeholder="Hours" type="number" />
          </div>
	</div>
  <button onClick={reset}>Reset</button> // 추가한 부분!!
	) 
	}
const root = document.getElementById("root");
ReactDOM.render(
          }
</script>

Converter 입력 부분 invert 기능 추가

<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 = () => setMinutes(0); // 추가한 부분!!
  	const onFlip = () => {
  		reset();
  		setFlipped((current) => !current_
  	}
  
	return (
		<div>
          <h1 className="hi">Super Converter</h1>
          <div>
            <label htmlFor="minutes">Minutes</label>
          	<input 
                   value={amount} 
                   id="minutes" 
                   placeholder="Minutes" 
                   type="number" 
                   onChange={onChange}
                   disabled={flipped} />
          	<label htmlFor="hours">Hours</label>
          	<input 
                   value={flipped ? amount : Math.found(amount / 60)} 
                   id="hours" 
                   placeholder="Hours" 
                   type="number" 
                   disabled={flipped === false} />
          </div>
	</div>
  <button onClick={reset}>Reset</button> // 추가한 부분!!
	) 
	}
const root = document.getElementById("root");
ReactDOM.render(
          }
</script>
  • flipped state를 추가한다.
  • 해당 state에 따라 hours value의 변환 여부, 입력 가능한 필드(disabled 옵션)를 변경한다!

Final Practice : Converter 분리 예제

<script type="text/babel">
  function MinutesToHours() {
  	// 기존 코드
  }
  function kmToMiles() {
  	// TODO
  }
  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" ? "Please select your units" : null}
      {index === "0" ? <MinutesToHours /> : null}
      {index === "1" ? <KmToMiles /> : null}
  </div>
  		)
  	}
  }
</script>
  • index state를 추가한다.
  • select 태그의 onChange 속성으로, 선택한 value에 따라 다른 컴포넌트를 렌더링!
profile
주니어 백엔드 개발자입니다! 조용한 시간에 읽고 쓰는 것을 좋아합니다 :)

0개의 댓글

관련 채용 정보