์ฃผ์ ์์
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- useState ์ค๊ณ ๋ฐ ๊ตฌํ
- function ํํ์ state๊ตฌํ
- state๋ฅผ destructuringํ๋ ๊ณผ์ ์ ํ์ ํ๊ณ ๊ตฌ์กฐ๋ถํด ํ ๋น์ ๋ํ ํด๊ฒฐ๋ฐฉ๋ฒ ๊ณ ์
- setterํจ์๋ฅผ ํตํ ์ํ ๊ฐฑ์
- ํ์ด๋ฒ ์์ฑ ๋จ๊ณ์์ ๊ฐฑ์ ๋ state๊ฐ์ ธ์ค๊ธฐ
- ๋ฉํ ๋ ๋ฆฌ๋ทฐ ๋ฐ์
- useState ์ค๊ณ ๋ฐ ๊ตฌํ
ํ์ต ํค์๋
- ์ฆ์ ์คํ ํจ์
- useState
๊ณ ๋ฏผ ๋ฐ ํด๊ฒฐ๊ณผ์
useState์ ๋์ ๋ฐฉ์๊ณผ ์ด์ ๋ฐ๋ฅธ ๋ ๋๋ง ๋ก์ง
๊ธฐ์กด์ ๋ด๊ฐ ์ง ๋ก์ง์ ๋ณด์
import { Root } from "../dom/DomRoot";
/**
*
* @param {any} initialState
* @returns {[any, function]}
*/
export function useState(initialState) {
let state = initialState;
const setState = (function () {
const hostRoot = Root();
// ํด๋ก์ ๋ก state์ ํด๋นํ๋ index๋ฅผ setState์ ๋ฌถ์ด๋๊ธฐ
return function (value) {
state = value;
hostRoot.reRender();
};
})();
return [state, setState];
}๋ด๊ฐ ์ฒ์ ์ง useState๋ฅผ ๋ณด๋ฉด initialState๋ฅผ state๋ผ๋ ๋ณ์์ ํ ๋นํด๋๊ณ ๋ฐํํ๋ฉฐ, setState๋ ์ฆ์ ์คํ ํจ์๋ฅผ ํตํด state์ ๊ฐ์ ๊ฐฑ์ ํด์ค ๋ค์ ๋ฆฌ๋ ๋๋ง์ ์ํค๋๋ก ๋ก์ง์ด ์ง์ฌ์ ธ ์๋ค.
ํ์ง๋ง ์ด๋ฌํ ๋ก์ง์ ๊ฒฝ์ฐ
- ์ฌ๋ฌ ๊ฐ์ state๊ด๋ฆฌ๊ฐ ๋ถ๊ฐ๋ฅ
- ํ state์ ์ฌ๋ฌ๋ฒ์ setState๊ฐ ์ผ์ด๋ ๊ฒฝ์ฐ ๋ฐ๋ณตํด์ ๋ฆฌ๋ ๋๋ง
- ๋ฆฌ๋ ๋๋งํ ํ์ ๊ฐฑ์ ๋ ๊ฒฐ๊ณผ๋ฅผ ์์ง ๋ชปํจ ๋ฑ์ ๋ฌธ์ ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ค์ ํ๋์ฉ ๋ณด์ํด๊ฐ๋ฉด์ ๊ตฌํ์ ์์ฑ์ํค๋ ค๊ณ ํ๋ค.
๋ฌธ์ : ๋ฆฌ๋ ๋๋งํ ํ์ ๊ฐฑ์ ๋ ๊ฒฐ๊ณผ๋ฅผ ์์ง ๋ชปํจ
ํด๋น ๋ฌธ์ ๋ state์์ ๊ฐ์ฅ ์ค์ํ๊ฒ ์๊ฐํ๋ ๋ฌธ์ ๋ผ ํด๋น ๋ฌธ์ ๋ถํฐ ํด๊ฒฐํด์ผ ํ๋ค. ๊ฐ์ด ๋ณํด๋ ๋ฆฌ๋ ๋๋ง ๊ณผ์ ์์ ์๋กญ๊ฒ ๋ฐ๋ ๊ฐ์ ๋ ๋๋ง์ํค์ง ์๋๋ค๋ฉด state์ ์๋ฏธ ์์ฒด๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ธฐ์กด ๋ด๊ฐ ๋ง๋ jsx ํ๋ก์ ํธ์ jsx ํธ๋์คํ์ผ๋ง๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด
import * as mhReact from "mhreact";
export default function Mainpage() {
var _mhReact$useState = mhReact.useState(1),
_mhReact$useState2 = _slicedToArray(_mhReact$useState, 2),
numbers = _mhReact$useState2[0],
setNumbers = _mhReact$useState2[1];
function onClickButton() {
console.log("click");
setNumbers(5);
}
return mhReact.createElement("div", null, mhReact.createElement("h1", null, "\uBBFC\uD615\uC774\uC758 \uD22C\uB450\uB9AC\uC2A4\uD2B8"), mhReact.createElement("input", {
type: "text",
placeholder: "\uD560 \uC77C\uC744 \uC4F0\uC790"
}), mhReact.createElement("button", {
onClick: onClickButton
}, numbers));
}์ด๋ฐ ์์ผ๋ก ๋ด๊ฐ useState๋ฅผ ํธ์ถํ์ ๋ ๋์ค๋ ๋ฆฌํด๊ฐ์ธ ๋ฐฐ์ด์ ๊ตฌ์กฐ๋ถํดํ ๋น ํ๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค. ์ด๋ฌํ ๊ตฌ์กฐ๋ถํด ํ ๋น์ ํตํด ์์๊ฐ์ state๋ก ์ค์ ํด๋์ numbers์ ๊ฒฝ์ฐ numbers์๋ ์์๊ฐ์ด ๋ค์ด์ค๊ฒ ๋๊ณ , ์์๊ฐ์ ๊ธฐ์กด์ ๊ฐ์ด ๋ณํด๋ ์๋กญ๊ฒ ํ ๋นํ์ง ์๋ ์ด์ ๋ณํ๋ฅผ ์ ์ ์๋ค๋ ์ ์ด ๋ฆฌ๋ ๋๋ง๋ element๊ฐ ๋ฐ๋ state๋ฅผ ์ ์ ์๋ ๋ฌธ์ ๋ฅผ ๋ณ์๋ค.
์๋1 : rootFiber์์ global๋ก state๊ด๋ฆฌ
rootFiber์ global property๋ฅผ ๋๊ณ ํด๋น ๊ฐ์ฒด์ states ํ๋กํผํฐ๋ฅผ ๋๋ ค๊ณ ํ๋๋ฐ rootFiber๊ฐ null๊ฐ์ด ๋์๋ค. ์๊ฐํด๋ณด๋ useState์ fiber๋ฅผ ๋๊ฒ ๋๋ฉด ๊ฐ์ฅ ์ฒ์ root๋ฅผ Renderํ ์์ ๋ง ํด๋ fiber๊ฐ ๋ง๋ค์ด์ง์ง ์์ ์ํ์ด๊ธฐ ๋๋ฌธ์ UseState์์์ ์ฑ๊ธํค ํจํด์ผ๋ก ๋ง๋ค์ด์ง rootFiber๋ฅผ ๊ฐ์ ธ์ค๊ฒ ๋๋ฉด null๊ฐ์ด ๋ฐ ์๋ฐ์ ์์๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด useState๋ฅผ ์ด๋์ ๊ฐ์ ธ์์ผ ํ ๊น?
์๋2: root์์ global๋ก state๊ด๋ฆฌ
root์์ global์ด๋ผ๋ ํ๋กํผํฐ๋ฅผ ๋๊ณ , ์ฌ๊ธฐ์ ๊ฐ state๋ฅผ ์ธ๋ฑ์ค : ๊ฐ์ ์์ผ๋ก ์ ์ฅํด๋์ผ๋ฉด ์ ์ญ์์ ๋ชจ๋ state๋ค์ ๊ด๋ฆฌํ ์ ์๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ด์ง ์์๊น ์๊ฐํ๋ค. ์ด๋ ๊ฒ ๋๋ฉด ๊ฐ ์ปดํฌ๋ํธ์์ ๋ง์ฝ ์ฌ์ฉํ๋ state์ ๊ฒฝ์ฐ์๋ transpiling ๊ณผ์ ์์ ํด๋น state๋ฅผ ํตํด ํด๋น ์ฐธ์กฐ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํด์ผ ํ๋ค.
state = root.global.states[index]๋ฐ๋ผ์ useState์ ์ด๋ฐ ์์ผ๋ก state๊ฐ์ ๋ด๋ณด๋ด๋๋ก ํ๋ ค๊ณ ํ๋๋ฐ, ๋ฌธ์ ๋ ํด๋ํ๋ ์ธ๋ฑ์ค๋ฅผ ํค๊ฐ์ผ๋ก ๊ฐ๋ value๊ฐ ๊ฐฑ์ ์ด ๋์์์๋ ๋ฆฌ๋ ๋๋ง ํ๋ ๊ณผ์ ์์ ์ ๋๋ก ์ฐธ์กฐ๋ฅผ ๋ชปํ๊ณ ์์๋ค. ์ด ๋ํ ๊ตฌ์กฐ๋ถํดํ ๋น์ผ๋ก ๊ฐ ๊ฐ์ ๊ณ์ ์์๊ฐ์ผ๋ก ๊ฐ์ง๊ฒ ๋์์ธ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
๋ฐ๋ผ์ ๋๋ state๋ฅผ ํจ์๋ก ๋ง๋ค์ด ๋ณด์๋ค.
import { Root } from "../dom/DomRoot";
import { RootFiber } from "../dom/rootFiber";
/**
*
* @param {any} initialState
* @returns {[any, function]}
*/
export function useState(initialState) {
const root = Root();
if (!root.global.hasOwnProperty("states")) {
root.global.states = {};
root.global.statesIdx = 0;
}
const index = root.global.statesIdx;
if (!root.global.states.hasOwnProperty(index)) {
root.global.states[index] = initialState;
}
// state = root.global.states[index]
const state = () => {
console.log('๋ฃจํธ',root);
return root.global.states[index]
}
const setState = (function () {
// ํด๋ก์ ๋ก state์ ํด๋นํ๋ index๋ฅผ setState์ ๋ฌถ์ด๋๊ธฐ
const currentIndex = index;
return function (value) {
if(typeof value === 'function'){
root.global.states[currentIndex] = value(root.global.states[currentIndex])
root.reRender();
} else{
root.global.states[currentIndex] = value;
console.log("state Changed", root.global.states[currentIndex]);
root.reRender();
}
};
})();
root.global.statesIdx++;
return [state, setState];
}ํจ์๋ฅผ ํ์ฉํ๊ฒ ๋ ๊ฒฝ์ฐ์๋ ๊ตฌ์กฐ๋ถํดํ ๋น์ด ์ด๋ฃจ์ด์ง์ง ์๊ธฐ ๋๋ฌธ์ root์ ๋ด๊ฒจ์๋ state๋ค์ ์ฐธ์กฐํ ์ ์๋ค.
ํ์ฌ๋ ํจ์๋ฅผ ํ์ฉํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์์ง๋ง ์ ์ ํ ์๋ก ๋ฌธ์ ๊ฐ ํ๋๋์ฉ ํ์ด๋์๋ค.
map์ด jsx๋ก ๋ณํ๋ ๋ ๋์ค์ ๋ณํํ๋ ๊ณผ์
<TodoList todoList={todo} />
...
export default function TodoList({ todoList }) {
return (
<ul>
{todoList.map((todo) => (
<li>{todo}</li>
))}
</ul>
);
}
todo๋ผ๋ ๋ฐฐ์ด๋ก ๋ state๋ฅผ ํ๋ ๋ง๋ค๊ณ ์ด๋ฅผ ๋ฆฌ์กํธ์ฒ๋ผ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ค์ ๋ฐฐ์ด์ ๋ํด map์ํจ ๊ฒฐ๊ณผ๋ฌผ์ ๋ชจ๋ ๋ ๋๋ง์ํค๋๋ก ๋ง๋ค์๋ค.
export default function TodoList(_ref) {
var todoList = _ref.todoList;
return mhReact.createElement("ul", null, todoList.map(function (todo) {
return mhReact.createElement("li", null, todo);
}));
}์ด์ ๋ํ ํธ๋์คํ์ผ๋ง ๊ฒฐ๊ณผ๋ฌผ์ ์์ ๊ฐ์ด ๋์จ๋ค.
mhReact.createElement(TodoList, {
todoList: todo
}));ํด๋น ํจ์๋ ๋ค๋ฅธ ์ปค์คํ ์ปดํฌ๋ํธ๋ค๊ณผ ๋๊ฐ์ด ์์์์ createElement pragma๊ฐ ๋ถ๋๋ค. ์ฌ๊ธฐ์ ๋ด๊ฐ ๋๊ฒจ์ค todo๋ผ๋ ๊ฐ์ด props๋ก ๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์์๋ค. ๋ฐ๋ผ์ ์ด๋ฐ props์ ๋ํด์ ๋ถ๊ธฐ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด ํ๊ทธ์ attribute๋ก ๊ฐ ์ ์๋ ๊ฒ๋ค์ ์ ์ธํ ๊ฒ๋ค์ ๋ฐ์ดํฐ๋ก ๋๊ฒจ์ค ์ ์์ด์ผ ํ๋ค.
export function createElement(type, props, ...children) {
props = devideValidProps(props);
if (typeof type === "function") {
if(type.name === "App") return;
if (props && props.hasOwnProperty("refs")) return type(props.refs);
return type();
}
return new MhElement(type, props, children.flat());
}์ฒ์์ ๋ถ๊ธฐ์ฒ๋ฆฌ ํด๋ณธ ๊ณณ์ createElement์๋ค. ํ์ง๋ง ์ฌ๊ธฐ์ ์ปดํฌ๋ํธ์ ๋ํด ํจ์๋ฅผ ๋น๋ฆฌ ์คํ์ํค๊ฒ ๋๋ค๋ฉด ์ํ๋ฅผ ์ฌ์ฉํ๋ ์ปค์คํ ์ปดํฌ๋ํธ์์๋ ์ด๋ฏธ ๊ตฌ์กฐ๋ถํด ํ ๋น์ด ์ด๋ฃจ์ด์ง ์ํ๋ก root์ node๋ค์ด ํ ๋น๋๊ธฐ ๋๋ฌธ์ ์ํ๊ฐ ๋ฐ์๋ ์ ์๋ค. ํนํ๋ map ํจ์๋ฅผ ๋๋ ๊ณณ์ด ์๋ค๋ฉด ๊ฒฐ๊ตญ map์ ํตํด ๋ชจ๋ element๊ฐ ๋ฏธ๋ฆฌ ๋ง๋ค์ด์ง๋ ์๋กญ๊ฒ ๋ ๋๋ง์ด ์ด๋ฃจ์ด์ง์ง ์๋๋ค.
๋ค์์ผ๋ก๋ ํจ์ํ ์ปดํฌ๋ํธ์ ๊ฒฝ์ฐ์๋ ํจ์ ๊ทธ๋๋ก ํ์ง ์๊ณ ๋๊ณ , ๋์ค์ ๋ ๋๋ง์ ํ๋ ๊ณผ์ ์์ fiber๋ฅผ ๋ง๋ค์ด๋ผ ๋ ํ์ด๋ด๋ฉด ๋์ง ์์๊น ์๊ฐํ๋ค.
export function destructuringFunctionalComponentWithRef(element){
const duplicated = {}
Object.entries(element.props.refs).forEach(([ref,val]) => {
if(typeof val === "function") duplicated[ref] = val();
})
return element.type(duplicated)
}์ด ๊ณผ์ ์์๋ ์๋๋ element์ ref๋ฅผ ๋ณ๊ฒฝํ๋ค๊ฐ ์ฐธ์กฐ๋์ด ์๋ ๊ฐ์ฒด์ ๊ฐ์ด ์๊ตฌํ ๋ณํ๋ ๋ฌธ์ ๋ ์์ด ๋ช์๊ฐ๋์ ์ฉ์ฉ๋งค๋ค duplicated๋ผ๋ ๊ฐ์ฒด ๋ฆฌํฐ๋ด์ ํตํด์ ์๋กญ๊ฒ ๋ณต์ฌํ์ฌ element๋ฅผ ๋ง๋ค์ด๋ด๋๋ก ํ๋ค.
์๋ฌดํผ ์ด ํจ์๋ createFiberFromElement์์ ์ด๋ฅผ destructuringํ ์ ์๋๋ก ํด์ฃผ์๋ค.
export function createFiberFromElement(
element,
parent = null,
index = 0,
key = null
) {
//์์์ state ์๋ ์ ๋ค์ ๋ค ํ์ด๋์
if (typeof element.type === "function") element = destructuringFunctionalComponentWithRef(element)
if (typeof element !== "object") {
parent.textNode = element;
return null;
}
...์ด๋ฅผ ํตํด functional component๋ฅผ ๋ง๋ฌ์ ๊ฒฝ์ฐ, ํด๋น ์ฐธ์กฐ๊ฐ์ ๋ฏธ๋ฆฌ destructuringํ์ง ์๊ฒ ํ๋๋ก ํ๊ธฐ ์ํด์ ๋ ๋๋ง์ ํ ๋๋ง๋ค ํ๊ณ ํจ์๋ฅผ ์คํํด์ state๊ฐ์ ๊ฐ์ ธ์ค๋๋ก ํ๋ค. ํ์ง๋ง state๋ฅผ ํจ์ํ์ผ๋ก ๋ง๋ค๋ฉด์ ์ฌ๋ฌ๊ฐ์ง ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๊ฐ์ฅ ํฐ ๋ฌธ์ ๋ setterํจ์์ ๋ํด์ ์ฝ๋ฐฑ์ ๋ฃ๊ฒ ๋๋ฉด prev๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ๋ ค๊ณ ํ๋๋ฐ, prev๊ฐ์ ์ฝ๋ฐฑ์ ์ธ์๋ก ๋ฃ๋ ๊ณผ์ ์์ state ํจ์ ์์ฒด๊ฐ ๋ค์ด๊ฐ๊ฒ ๋๋ฏ๋ก, ์ ์์ ์ธ ๊ฐฑ์ ์ด ์๋ ํจ์ํ์ผ๋ก๋ง ๊ฐฑ์ ์ด ๋์๋ค.
const setState = (function () {
// ํด๋ก์ ๋ก state์ ํด๋นํ๋ index๋ฅผ setState์ ๋ฌถ์ด๋๊ธฐ
const currentIndex = index;
return function (value) {
if(typeof value === 'function'){
root.global.states[currentIndex] = value(root.global.states[currentIndex])
root.reRender();
} else{
root.global.states[currentIndex] = value;
console.log("state Changed", root.global.states[currentIndex]);
root.reRender();
}
};
})();์์ง ํด๋น ํจ์์ prev๋ ์ ๋๋ก ๋์ํ์ง ์๋๋ฐ, ์ด๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํด์ผ ํ ์ง ๊ณ์ ๊ณ ๋ฏผ์ค์ ์๋ค..