클로저를 사용하여 useState 구현하기

JaeHong Jeong·2023년 7월 24일
0

React

목록 보기
4/5
post-thumbnail

개요

클로저를 활용하여 리액트훅 useState를 구현하는 포스팅

리액트에서 제공하는 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;

useState 구현하기

hooks.jsx 기본 세팅

  • useState를 동작을 추상하기
// 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 };
})(); // 클로저를 활용 즉시실행

state

  • CASE 1 : state에 저장된 값이 없을 경우 initialValue 저장
  • CASE 2 : state에 저장된 값이 있을 경우 savedValue
  • useState 함수 내부에 state를 선언하면 초기화가 된다 그래서 외부 변수에 만들고 거기에 저장 ( 클로저 )
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

setState

  • 저장된 값을 인자로 받은 값으로 바꾼다
  • 리렌더링
// 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
  • 화면에서 INCREASE 버튼을 누르면 count의 값은 바뀌지 않고 hooks 배열에 1이 추가된다
  • App.jsx에서 useState 호출이 되고 화면이 렌더링 된 후 setState가 호출된다 그러므로 그 때 hookIndex는 2(제일 마지막 Index)이다.
  • 그래서 0번에 들어있는 count값을 바꾸지 못하고 마지막 Index에서 값이 들어간다.
  • 결론은 setCount일 경우 ( 첫번째를 바꾸고 싶을 경우 ) Index가 0으로 고정되어 있어야 한다.
  • 그리고 setToggle ( 두번째 useState )일 경우는 Index가 1로 고정되어 있어야 한다.
  • 하지만 hookIndex는 계속계속 변한다.
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 };
})();
  • 클로저를 이용하여 Index값 고정시켜버리기
  • 1이 배열 마지막에 들어가지 않고 0을 1로 업데이트시켰다
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 };
})();
profile
반갑습니다.

0개의 댓글