1. 값을 바꾸면 자동 재 렌더링
function Counter() {
// let count = 0;
// 여기 변수는 마음대로 작성이 가능하다. 하지만 그 기능에 맞게 작성하는 것이 좋다.
const [count, setCount] = Index.useState(10);
// const로 바꿔야 하고, 값만 바꾸는 것이 아닌 메서드 통해 변경을 해야한다.
const handleDown = () => {
setCount(count - 1);
};
const handleUp = () => {
setCount(count + 1);
};
const handleReset = event => {
setCount(0);
};
return (
Index.createElement('div', { id: 'counter' },
Index.createElement('button', { type: 'button', onclick: handleDown }, '-'),
Index.createElement('button', { type: 'button', onclick: (event) => handleReset(event) }, 0),
Index.createElement('button', { type: 'button', onclick: handleUp }, '+'),
Index.createElement('span', null, count))
);
}
// 애플리케이션의 시작점
function App() {
return (
Index.createElement('div', { id: 'app' }, Header, Counter)
);
}
// document.getElementById('root').appendChild(App);
Index.createRoot(docu
html에서 JS 부분은 이러하게 작성이 되었다. 이는 이제 데이터 변경 시 자동으로 렌더링이 되게 하려고 한다.
이제 index.js에서 틀을 만들어주었는데,
아래와 같이 함수를 하는데, _root와 _stateValue는 이제 외부에서 사용하지 않도록 사용한 암묵적인 변수인다. 하지만 여기 파일에서는 이를 함수 밖에서도 사용이 가능했지만, 함수 안에서 사용하도록 구현했다. 여기서 즉시실행함수는 정의가 되면 바로 실행되는 함수이며, 전역 변수를 오염시키지 않는다. 또한 코드의 충돌을 방지하고, 모듈화를 할 수 있다는 장점을 가졌다. 근데 이는 한번 호출되면 다시 호출이 되지 않는다. 하지만 반환된 객체나 함수로는 재 사용이 가능하다.
그래서 index객체에 특정 기능들을 반환하는데, index에 할당이 되어, index.createElement 등으로 함수들을 호출하며 사용할 수 있다.
const index = (() => {
// 이 안에서 사용하고자 하면 즉시실행 함수
// 즉시실행함수를 만들게 되면 함수 내부에서 변수를 선언하면 외부에서 영향을 주지 않는다.
// (() => {})();
// (function(){})();
let _root;
let _stateValue;
// 변수명 앞에 _ 붙이면 외부애서 사용하지말라는 암묵적인 룰임
// 지정한 속성과 자식 노드를 가지는 요소 노드를 생성해서 반환
// <button type="button" onclick="handleUp()">+</button>
// createElement('button', {type: 'button', onclick: 'handleUp()'}, '+');
const createElement = (tag, props, ...children) => {
// 요소 노드 생성
const elem = document.createElement(tag);
// 속성 추가
if (props) {
// 배열이면 for of
// 객체면 for in
// 객체 속성의 개수만큼
for (const attrName in props) {
const value = props[attrName];
if (attrName.toLowerCase().startsWith('on')) {
elem.addEventListener(attrName.toLowerCase().substring(2), value);
} else {
elem.setAttribute(attrName, value);
}
}
}
// 자식 노드 추가
for (let child of children) {
if (typeof child === 'string' || typeof child === 'number') {
child = document.createTextNode(child);
} else if (typeof child === 'function') {
child = child();
}
elem.appendChild(child);
}
return elem;
};
이는 초기값을 설정하는데,
// 상태값 관리
// let [count, setCount] = Index.useState(10);
const useState = (initValue) => {
// 최초에 한번만 initValue 값으로 저장하고 useState가 다시 호출되면 initValue는 무시하고 저장된 값을 사용
if (_stateValue === undefined) {
// 최초 useState가 호출될 때 한번만 실행
_stateValue = initValue;
}
// setValue(11);
function setValue(newValue) {
const oldValue = _stateValue; // 10
_stateValue = newValue; // 11
// Object.is는 두 값이 같은지 비교해서 같지 않을 경우에(상태가 변경된 경우) 리렌더링 한다.
if (!Object.is(oldValue, newValue)) {
_root.render();
}
}
return [_stateValue, setValue];
};
return { createElement, createRoot, useState };
})();
1. 초기 상태값 설정:
- useState가 처음 호출될 때 initValue를 _stateValue에 저장한다.
- 이후 useState가 다시 호출되더라도 이미 _stateValue에 값이 저장되어 있으므로 초기화하지 않는다. 따라서, 상태는 계속 유지된다.
if (_stateValue === undefined) {
_stateValue = initValue;
}
2. 상태 변경 함수 - setValue
function setValue(newValue) {
const oldValue = _stateValue;
_stateValue = newValue;
- setValue는 새로운 값을 받아 현재 상태를 업데이트 하는 함수이다.
- 변경할 새로운 값 newValue를 인수 받아 _stateValue를 업데이트 한다.
3. 상태 변경 확인 후 렌더링 - Object.is
if (!Object.is(oldValue, newValue)) {
_root.render();
}
- Object.is 메서드는 oldValue와 newValue가 동일한지 비교한다. 값이 다를 때만 render 함수를 호출하여 컴포넌트를 다시 렌더링한다.
- 이를 통해 상태가 변할때만 다시 렌더링하여 불필요한 렌더링을 방지한다.
4. return
return [_stateValue, setValue];
- useState 함수는 [현자 상태값, 상태 변경 함수] 형태의 배열을 반환한다.
- 이렇게 반환된 값을 구조분해할당 (let [count, setCount] = useState(10);) 으로 받아 실행할 수 있다.
이제까지 한 것은 이제 어찌보면 라이브러리를 하드코딩식으로 진행한 코드이다. 자바스크립트는 이렇게 복잡하지만, 리액트의 경우는 엄청 간단하게 할 수 있다.
아래는 리액트이다.
코드를 보면 아주 간단하다. 컴포넌트화를 하고, 그 안에 기능 및 html 관련된 걸 넣어주면 된다.
이 아래는 JSX로 작성이 되었는데, JSX는 React에서 사용하는 JS 확장 문법이다. 그래서 이는 HTML과 비슷한 형태로 컴포넌트를 작성할 수 있게 도와준다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Counter</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Header() {
return (
<header>
<h1>Counter - React</h1>
<p>파일 경로: <span id="filepath">{`ch${document.URL.split('/ch')[1]}index.html`}</span></p>
</header>
)
}
function Counter() {
let [count, setCount] = React.useState(0);
const handleDown = () => {
setCount(count - 1);
};
const handleUp = () => {
setCount(count + 1);
};
const handleReset = event => {
setCount(0);
};
return (
<div id="counter">
<button type="button" onClick={handleDown}>-</button>
<button type="button" onClick={(event) => handleReset(event)}>0</button>
<button type="button" onClick={handleUp}>+</button>
<span>{count}</span>
</div>
)
}
function App() {
return (
<div id="app">
<Header />
<Counter />
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
'JavaScript' 카테고리의 다른 글
[모던자바스크립트 Deep Dive] 45장. 프로미스 (0) | 2024.11.14 |
---|---|
map, forEach, for of, ArrowFunction (0) | 2024.11.13 |
[vanilla practice] Counter 실습 (0) | 2024.11.07 |
[vanilla practice] Todo List 실습 (1) | 2024.11.06 |
key 저장 하는 법 (0) | 2024.10.25 |