한 줄로 요약 하자면, 상태의 변경을 관찰하겠다.
상태가 변경되면 미리 정의한 일련의 동작을 실행시키는 원리이다.
/core/State.js
// 여기서 state 변수는 json 객체이어야 한다.
export const State = (state) => {
// 구독자는 SET 자료구조에 담는다.
state._subscribers = new Set();
// 구독하는 메소드이다. SET에 추후에 실행시킬 메소드를 추가한다.
state.subscribe = (render) => state._subscribers.add(render);
// state의 모든 key에 대해서 작업을 수행한다.
Object.keys(state).forEach((key) => {
let _value = state[key];
// C++ 연산자 오버로딩과 비슷하다. `=`연산의 동작을 재정의한다.
Object.defineProperty(state, key, {
get() {
return _value;
},
set(value) {
// 상태가 실재로 변동되지않으면 로직을 수행하지 않는다.
if (_value === value) return;
if (JSON.stringify(_value) === JSON.stringify(value)) return;
_value = value;
// 구독한 메소드를 모두 실행시킨다.
state._subscribers.forEach((fn) => fn());
},
});
});
return state;
};
/core/index.js
export * from "./State";
상태를 정의한다.
export const homeState = State({ list: [] });
구독를 추가한다.
_subscribers
은 Set을 활용하여 구독자를 관리한다.
상태변경시 실행시킬 작업을 등록한다.
const render = () => {
root.innerHTML = `
${homeState.list.map((name) => `<p>${name}</p>`).join("")}
`;
};
homeState.subscribe(render);
상태를 변경한다.
homeState.list = [...homeState.list, e.target.name.value];
defineProperty를 사용하여 =
로 state에 새로운 값을 주입하면 set
메소드가 실행된다.
/pages/home/_state.js
import { State } from "../../core";
export const homeState = State({ list: [] });
이렇게 전역 상태로 사용해도 되고, 컴포넌트 내부에서 선언해서 props로 넘겨줘도 좋을 것 같다.
/pages/home/HomeForm.js
import { homeState } from "./_state";
export const HomeForm = () => {
const root = document.createElement("form");
root.innerHTML = `
<h1>Form</h1>
<input id="name"/>
`;
root.addEventListener("submit", (e) => {
e.preventDefault();
homeState.list = [...homeState.list, e.target.name.value];
e.target.name.value = "";
});
return root;
};
/pages/home/HomeList.js
import { homeState } from "./_state";
export const HomeList = () => {
const root = document.createElement("div");
const render = () => {
root.innerHTML = `
${homeState.list.map((name) => `<p>${name}</p>`).join("")}
`;
};
homeState.subscribe(render);
render();
return root;
};
/pages/home/index.js
export const HomePage = () => {
const app = document.querySelector("#app");
app.appendChild(HomeForm());
app.appendChild(HomeList());
};
/pages/index.js
import "../styles/index.scss";
import { HomePage } from "./home";
(() => {
HomePage();
})();
export const State = (state) => {
state._subscribers = new Set();
state.subscribe = (render) => state._subscribers.add(render);
Object.keys(state).forEach((key) => {
let _value = state[key];
Object.defineProperty(state, key, {
get() {
return _value;
},
set(value) {
if (_value === value) return;
if (JSON.stringify(_value) === JSON.stringify(value)) return;
_value = value;
state._subscribers.forEach((fn) => fn());
},
});
});
return state;
};