Vanilla JS와 React JS로 click me 버튼을 클릭하면 total clicks의 수가 하나씩 증가하는 기능을 각각 만들어 비교해보자.
Vanilla JS
< body >
< h3 > total clicks : 0 </ h3 >
< button id =" button "> click me </ button >
</ body >
< script >
const button = document . getElementById ( ' button ' ) ;
const h3 = document . querySelector ( ' h3 ' ) ;
let counter = 0 ;
function handleClick () {
counter += 1 ;
h3 . innerText = ` total clicks : ${ counter }` ;
}
button . addEventListener ( ' click ' , handleClick ) ;
</ script >
Vanilla JS는 html을 만들고 js로 가져와서 다시 html을 수정하는 방식이다.
하지만 React JS는 처음부터 js에서 시작하여 html을 만들기에 html 코드를 직접 작성할 필요가 없다.
React JS로 element를 직접 생성할 수 있기 때문이다.
따라서 React JS는 html의 element를 바로 업데이트 할 수 있어 interactive application 을 만들기에 좋다.
또한 React JS는 UI에서 바뀐 부분만 업데이트 된다는 장점이 있다.
React JS - 1) createElement
createElement를 사용하는 방법은 개발자들이 거의 사용하지 않는 방법이다.
좀 더 쉽고 생산적인 방법이 있기 때문이다.
하지만 먼저 이 방법을 이해해야 React JS의 본질을 이해할 수 있다.
React JS는 interactive의 원동력이며, React DOM은 React element 를 html로 바꿔준다.
< body >
< div id =" root "></ div >
</ body >
< script >
let counter = 0 ;
const root = document . getElementById ( ' root ' ) ;
const handleClick = () => {
counter += 1 ;
render () ; // 클릭할 때마다 render() 함수를 호출하여 UI를 업데이트
};
const render = () => {
const h3 = React . createElement ( ' h3 ' , null, ` total clicks : ${ counter }` ) ;
const button = React . createElement ( ' button ' , { onClick : handleClick }, ' click me ' ) ; // onClick 속성에 handleClick 함수 전달
const app = React . createElement ( ' div ' , null, [ h3 , button ]) ;
ReactDOM . render ( app , root ) ;
};
render () ; // 초기 UI 렌더링
</ script >
React JS - 2) JSX
createElement를 대체하려는 이유는 개발자들에게 좀 더 편리한 JSX를 사용하기 위함이다. JSX는 JS를 확장한 것으로 html 규칙과 비슷한 문법으로 React 요소를 생성할 수 있어 개발자들에게 더 편하다. 그런데 JSX로 적은 코드를 브라우저가 이해할 수 있게 1) 방법으로 바꿔주는 babel 이 필요하다.
< body >
< div id =" root "></ div >
</ body >
< script type =" text/babel ">
const root = document . getElementById ( ' root ' ) ;
let counter = 0 ;
const handleClick = () => {
counter += 1 ;
render () ; // 클릭할 때마다 render() 함수를 호출하여 UI를 업데이트
};
const render = () => {
ReactDOM . render ( < App />, root ) ;
};
const H3 = () => (
< h3 > total clicks : { counter }</ h3 >
) ;
const Button = () => (
< button onClick ={() => handleClick () }> click me </ button >
) ;
const App = () => (
< div >
< H3 />
< Button />
</ div >
) ;
render () ; // 초기 UI 렌더링
</ script >
여기서 꼭 체크해야할 매우 중요한 것은 컴포넌트의 첫문자는 항상 대문자로 시작해야만 한다는 것이다.
소문자로 한다면 html 태그로 인식하게 된다.
H3와 Button를 따로 함수로 만들지 않고 그냥 Container 안에 포함시킬 수도 있다.
< body >
< div id =" root "></ div >
</ body >
< script type =" text/babel ">
const root = document . getElementById ( ' root ' ) ;
let counter = 0 ;
const handleClick = () => {
counter += 1 ;
render () ; // 클릭할 때마다 render() 함수를 호출하여 UI를 업데이트
};
const render = () => {
ReactDOM . render ( < App />, root ) ;
};
const App = () => (
< div >
< h3 > total clicks : { counter }</ h3 >
< button onClick ={() => handleClick () }> click me </ button >
</ div >
) ;
render () ; // 초기 UI 렌더링
</ script >
그런데 이는 계속해서 App를 render 해주기 때문에 최고의 방법은 아니다.
어떤 페이지나 알림 등이 있는데 이를 계속 호출해야 되는 것은 비효율적이기 때문이다.
React JS - 3) useState
계속 함수를 부를 필요 없이 React JS 내에서 데이터를 보관하고 자동으로 rerendering 하는 방법이다.
const [현재값 state, state를 수정하는 modifier 함수] = React.useState(초기값);
state를 수정하는 함수는 보통 set+변수명 으로 설정한다.
< body >
< div id =" root "></ div >
</ body >
< script type =" text/babel ">
const root = document . getElementById ( " root " ) ;
function App (){
const [ counter , setCounter ] = React . useState ( 0 ) ;
const handleClick = () => {
setCounter ( (cur) => cur + 1 ) ;
};
return (
< div >
< h3 > total clicks : { counter }</ h3 >
< button onClick ={ handleClick }> click me </ button >
</ div >
) ;
}
ReactDOM . render ( < App />, root ) ;
</ script >
modifier 함수가 state를 바꾸면 App component가 재생성되고 코드가 재실행되는데 이 때 return은 새로운 값을 가지고 재실행이 된다.
즉, state가 바뀌면 rerendering이 일어난다는 것이며, rerendering 될 때 React JS는 UI가 업데이트가 된 부분만 달라지게 한다.
여기서 handleClick 안의 setCounter를 다음과 같이 직접 할당 방법으로 수정해도 결과는 변함없다.
하지만 이는 현재 state와 관련없는 값을 새로운 state로 하고 싶은 경우에 해당한다.
setState()는 component를 항상 즉각적으로 갱신하지 않기 때문에 예상치 못한 업데이트가 어딘가에서 일어난다면 잠재적인 문제가 될 수 있으므로 이전 state 값을 기준으로 state를 설정해야 한다면 직접 할당이 아닌 함수형으로 넣어주는 것이 더 안전하다.