[JavaScript] JavaScript로 React.useState를 구현하려면 어떻게 해야 할까?

telnet turtle·2022년 10월 20일
0

글의 제목은 거창할 수 있으나 내용은 아니다. React.useState는 pure JS 함수 호출인데 어떻게 상태를 저장하는 기능이 있을까? 내가 직접 만들어 보자.

먼저 React.useState는 세 가지 기능을 갖고 있다. 상태를 저장하고, 변경하는 함수를 리턴하며, 상태를 변경하는 함수를 호출하면 컴포넌트를 다시 렌더링한다. 컴포넌트를 재렌더링 하는 기능은 React API를 이용할 것 같다. 부끄럽게도 아직 찾아보지는 않았다. 상태 저장 부분을 먼저 생각해보자. 자바스크립트의 클로저를 이용하면 되지 않을까?

생각나는 대로 구현해 보자.

첫 번째 버전:

function useState(first) {
  let state = first;
  function setState(next) {
    state = next;
  }
  return [state, setState];
}

let [a, setA] = useState(10);
let [b, setB] = useState(11);
let [c, setC] = useState(12);

console.log({ a, b, c });

setA(20);

console.log({ a, b, c });

이 프로그램의 출력은:

{ a: 10, b: 11, c: 12 }
{ a: 10, b: 11, c: 12 }

상태 A가 업데이트되지 않았다. 그 이유는 변수 a에 primitive value를 대입했기 때문에, 레퍼런스가 유지되지 않고, 값이 대입되었기 때문이다. (혹은 리터럴.)

그렇다면 레퍼런스를 유지하도록 객체로 둘러싸면 될 것이다.

두 번째 버전이다.

function useState(first) {
  let state = { state: first };
  function get() {
    return state.state;
  }
  function set(next) {
    state.state = next;
  }
  return [get, set];
}

let [getA, setA] = useState(10);
let [getB, setB] = useState(11);
let [getC, setC] = useState(12);

console.log({ a: getA(), b: getB(), c: getC() });

setA(20);

console.log({ a: getA(), b: getB(), c: getC() });

이 프로그램의 출력은:

{ a: 10, b: 11, c: 12 }
{ a: 20, b: 11, c: 12 }

됐다!

바뀐 점은, 먼저 객체로 상태를 래핑하여서 변수에 레퍼런스를 담아, 레퍼런스를 유지될 수 있도록 했고, 또 a.state 처럼 접근하기 싫어서 getter 함수로 변경하였다. 5분 정도 걸렸다.

다만 지금의 수제 useState 함수를 실제로 react 컴포넌트에서 사용하면 문제가 발생한다. setter 함수로 값을 업데이트하는 것은 성공했지만, 함수 컴포넌트가 재렌더링 되며 다시 useState를 호출하기 때문에, 기존의 state 값이 유지되는 대신 새로운 상태를 만드는 행위가 된다. 이를 해결하려면 React 내부적으로 hooks를 관리하는 방법인 배열을 이용해봐야 할 것 같다. 참고로 배열을 이용하는 이유는 hooks의 디자인 철학과 연관되어 있다. 한 컴포넌트 안에서 hooks 들의 숫자나 순서는 변하지 않는다. React가 hook을 처음 소개할 때 쓰인 문서를 읽어보면 관련 내용이 쓰여있다.

TBU

profile
프론트엔드 엔지니어

0개의 댓글