[노마드/ReactJS 영화 웹 만들기] - Ch03 | 0. State?
ReactJS State
바닐라 JS에서 버튼을 클릭하면 Count가 올라가는 함수를 이벤트 리스너와 함께 구현하면 다음과 같다.
<body>
<span>클릭된 숫자: 0</span>
<button id="btn">클릭</button>
</body>
<script>
let count = 0;
let span = document.querySelector('span');
const btn = document.getElementById('btn');
function handleClick() {
console.log('클릭');
count = count + 1;
span.innerText = `클릭된 숫자: ${count}`;
}
btn.addEventListener('click', handleClick);
</script>
바닐라 JS와 ReactJS는 값이 변경될 때 UI가 어떻게 rerender
되는지 확연하게 구분된다.
ReactJS는 UI에서 바뀐 부분만 업데이트해주고,
바닐라 JS는 변경된 태그 전체를 업데이트한다.
ReactJS로 변환
위 바닐라 JS코드를 ReactJS로 변환해 보자면
<!--ReactJS 구현부-->
<script type="text/babel">
const root = document.getElementById('root');
let counter = 0;
function countUp() {
counter = counter + 1;
console.log(counter);
}
const Container = () => (
<div>
<h3 id="main-title">클릭 횟수: {counter}</h3>
<button id="new-btn" onClick={countUp}>
카운트 올리기
</button>
</div>
);
ReactDOM.render(<Container />, root);
</script>
오류 확인
하지만 버튼을 클릭하면, 클릭 이벤트는 정상적으로 작동하지만 UI에 반영되지는 않는다.
웹 페이지가 처음 실행되면 Container
은 함수이기 때문에 바로 실행되지는 않고, 페이지가 로드되면서 Container
컴포넌트가 딱 한 번 랜더링 된다.
이벤트 리스너를 등록하고 클릭하면 conutUP
함수가 실행되지만, 최초로 Container
를 랜더링 하고 더 이상 rerender
해주지 않았기 때문에 UI가 업데이트되지 않는 것이다.
해결 방법
이를 해결하기 위해서는 conutUP
함수가 실행될 때마다 Container
컴포넌트를 rerender
해줘야 한다.
function countUp() {
counter = counter + 1;
console.log(counter);
ReactDOM.render(<Container />, root);
}
랜더링 순서
- 애플리케이션 실행 ➜
ReactDOM 랜더
➜counter = 0
➜- 버튼 클릭 ➜
- 함수 실행
(countUp())
➜ ReactDOM re-render
랜더링 함수 생성
그렇다면 ReactDOM.render(<Container />, root);
는 사주 사용할 거 같기 때문에 함수로 만들어 주면 더 깔끔하고 재사용이 쉬워진다.
function countUp() {
counter = counter + 1;
console.log(counter);
render();
}
function render() {
ReactDOM.render(<Container />, root);
}
//ReactDOM.render(<Container />, root); ➜
render()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<!--Babel 스크립트-->
<script crossorigin src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!--ReactJS 구현부-->
<script type="text/babel">
const root = document.getElementById('root');
let counter = 0;
function countUp() {
counter = counter + 1;
console.log(counter);
render();
}
function render() {
ReactDOM.render(<Container />, root);
}
const Container = () => (
<div>
<h3 id="main-title">클릭 횟수: {counter}</h3>
<button id="new-btn" onClick={countUp}>
카운트 올리기
</button>
</div>
);
render();
</script>
</html>
하지만! render
함수를 만들어 사용하는 방법이 최고의 방법은 아니다! 더 쉬운 방법이 있는데 왜냐하면 값이 바뀔 때마다 render()
함수를 넣어줄 순 없는 노릇이기 때문이다. 다음 포스트에서 이것을 어떻게 개선해 나아갈지 알아보자
React18 버전
React 18 버전부터는 createRoot
를 사용해야 한다. 우선 Container
를 전역에서 한번, 버튼 클릭할 때마다 rendering
을 해야 하는데ReactDOMClient.createRoot(root).render()
를 두 곳에 모두 쓸 경우와 function render()
를 호출한 경우 모두 작동은 되지만 console
창에 ReactDOMClient.createRoot()
대신 root.render()
를 호출하라고 뜨게 된다.
React18부터는 ReactDOMClient.createRoot()
를 변수에 담아 사용해야 한다.
const root = ReactDOM.createRoot(document.getElementById("root"));
라고 root
변수를 만들어 주고, render
함수를 만들지 않은 상태로 렌더링이 필요한 부분에 root.render();
로 명명해 주면 된다.
const root = ReactDOM.createRoot(document.getElementById('root'));
let counter = 0;
function countUp() {
counter++;
Render();
}
function Render() {
return root.render(<Container />);
}
const Container = () => (
<div>
<h3 id="main-title">클릭 횟수: {counter}</h3>
<button id="new-btn" onClick={countUp}>
카운트 올리기
</button>
</div>
);
Render();