클로저를 활용하여 리액트훅 useState를 구현하는 포스팅
// App.jsx
import { useEffect } from "react";
import { useState } from "react"; // useState 구현 후 import { useState } from "./hooks";
const App = () => {
const [count, setCount] = useState(0);
const [toggle, setToggle] = useState(false);
useEffect(() => console.log("render"));
useEffect(() => console.log("count changed: ", count), [count]);
useEffect(() => console.log("toggle changed: ", toggle), [toggle]);
useEffect(
() => console.log("count or toggle changed: ", count, toggle),
[count, toggle]
);
useEffect(() => console.log("-------------"));
return (
<div>
<h1>count:{count}</h1>
<button onClick={() => setCount(count + 1)}>INCREASE</button>
<h1>current Toggle: {toggle.toString()}</h1>
<button onClick={() => setToggle(!toggle)}>toggle</button>
</div>
);
};
export default App;
// hooks.jsx
export const { useState } = (function makeMyhooks() {
// interface : 추상하기
// [state, setState] : useState는 튜플형태로 state와 setState를 리턴한다
// state : initialValue || saved state ( state는 초기값 or 저장된 값을 리턴한다 )
/*
setState(value) : void ( setState는 value를 받아 아무것도 리턴하지 않는다 )
saved state = value: ( 저장된 값에 인자로 받은 value를 재할당 )
re-render : 그리고 리렌더링까지 시켜준다
*/
function useState() {}
return { useState };
})(); // 클로저를 활용 즉시실행
export const { useState } = (function makeMyhooks() {
// useState 함수에 저장하면 초기화가 된다 그래서 외부에 변수를 만들고 거기에 저장을 한다 (**클로저를 활용**)
// useState는 여러개 호출가능 -> 여러개의 state값을 저장할 수 있어야한다 -> 배열
// 여러값을 저정하려면 순서대로 저장해야함 -> 여러개의 state 중에 현재의 hookIndex가 몇인지에 해당하는 변수도 선언
const hooks = []; // state들을 저장할 배열
let hookIndex = 0;
function useState(initialValue) {
if (hooks[hookIndex] === undefined) { // 첫번째 호출이란 뜻 -> 값을 저장을 해줘야한다 (initialValue)
hooks[hookIndex] = initialValue;
}
const state = hooks[hookIndex];
const setState = undefined;
hookIndex++; // hooks 배열에 여러 state를 넣어주기 위해 리턴하기 전 hookIndex를 +1 해준다
return [state, setState];
}
return { useState };
})();
💡 hookIndex++를 해줘야하는 이유
// hooks: [], hookIndx: 0
const [count, setCount] = useState(0);
// hooks: [0], hookIndex: 0
const [toggle, setToggle] = useState(false);
// hooks: [false], hookIndex: 0
// hooks: [], hookIndx: 0
const [count, setCount] = useState(0);
// hooks: [0], hookIndex: 1
const [toggle, setToggle] = useState(false);
// hooks: [0, false], hookIndex: 2
// main.jsx
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
export function render() {
ReactDOM.createRoot(document.getElementById("root").render(<App />));
} // main.jsx에서 render함수 만들어서 export
count
의 값은 바뀌지 않고 hooks 배열에 1이 추가된다App.jsx
에서 useState 호출이 되고 화면이 렌더링 된 후 setState가 호출된다 그러므로 그 때 hookIndex는 2(제일 마지막 Index)이다.import { render } from "./main";
export const { useState } = (function makeMyhooks() {
const hooks = [];
let hookIndex = 0;
function useState(initialValue) {
if (hooks[hookIndex] === undefined) {
hooks[hookIndex] = initialValue;
}
const state = hooks[hookIndex];
console.log("hooks", hooks);
const setState = function (value) {
hooks[hookIndex] = value; // 이 때 hookIndex === 2 이다. 당연히 배열의 2번 Index에 count + 1 값이 들어간다
hookIndex = 0;
render();
};
hookIndex++;
return [state, setState];
}
return { useState };
})();
import { render } from "./main";
export const { useState } = (function makeMyhooks() {
const hooks = [];
let hookIndex = 0;
function useState(initialValue) {
if (hooks[hookIndex] === undefined) {
hooks[hookIndex] = initialValue;
}
const state = hooks[hookIndex];
const setState = (function () { // setState는 함수를 즉시실행하는 리턴 값, 즉시 실행함수는 useState가 호출될 때 실행
const currentIndex = hookIndex; // hookIndex가 얼마로 바뀌던 currentIndex값은 고정되어 있음 -> 호출된 당시에 hookIndex값을 currentIndex값에 저장을 해놨으니까
return function (value) {
hooks[currentIndex] = value;
hookIndex = 0;
render();
};
})();
hookIndex++;
return [state, setState];
}
return { useState };
})();