useState는 왜 const로 선언될까요?

React에서 자주 사용하게되는 Hook인 useState문
보통 useState문은 다음과 같이 사용하게 됩니다.

const [number, changeNumber] = useState(0);

그리고 우리는 changeNumber를 통해서 number를 바꾸게 됩니다.

changeNumber(prevNumber => number + 1);

어라? 근데 이상합니다. 분명 const는 재할당이 되지 않는 상수를 선언하는 것으로 알고 있는데,
어떻게 const로 선언된 변수인 number의 값이 변할 수 있을까요?

number의 값이 바뀔 수 있다면 우리는 재할당이 가능한 let으로 useState를 사용해야 하는 것이 아닐까요?


아닙니다.

기본적으로 자바스크립트의 클로저의 대한 개념이 필요합니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures

클로저 - JavaScript | MDN

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.

developer.mozilla.org

    function makeFunc() {
      var name = "Mozilla";
      function displayName() {
        alert(name);
      }
      return displayName;
    }

    var myFunc = makeFunc();
    //myFunc변수에 displayName을 리턴함
    //유효범위의 어휘적 환경을 유지
    myFunc();
    //리턴된 displayName 함수를 실행(name 변수에 접근)

해당 예제에서 주의깊게 확인해야하는 점은, myFunc라는 변수에 makeFunc가 리턴되어 저장된다는 것 입니다.
그럼 var myFunc = makeFunc(); 즉, myFunc의 선언 이후에도 makeFunc 내부에 있는 name에 접근이 가능할까요?
가능합니다. myFunc에 makeFunc 참조가 유지되어있기 때문입니다.

이것을 우리는 Closure, 클로져라고 합니다.
그렇기에 우리는 클로져를 다음과 같이 정의합니다.

함수가 속한 렉시컬 스코프를 기억하여, 함수가 렉시컬 스코프 밖에서 실행될 때 해당 스코프에 접근하는 기능


그렇다면, 다시 React로 돌아오겠습니다.

https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/

[번역] 심층 분석: React Hook은 실제로 어떻게 동작할까?

React Hook에 대해 이해하려면 JavsScript 클로저에 대해 잘 알아야합니다. React의 작은 복제본을 만들어보며 클로저와 hook의 동작 방식을 알아봅니다.

hewonjeong.github.io

https://dudghsx.tistory.com/18

hooks에서 useState가 const로 선언되는 이유

 카카오톡 오픈 채팅방에 따르면 요즘의 기업들은 클래스형이 아닌 훅스를 사용한다고 한다. 남들하는건 다 할줄 알아야지.. 그래서 공식 문서를 하나씩 읽어보기 시작했다. ko.reactjs.org/docs/hooks

dudghsx.tistory.com

* 위의 글을 읽으시면 더욱 자세하게 알 수 있습니다.

const MyReact = (function () {
  let hooks = [],
    currentHook = 0;
  return {
    render(Component) {
      const Comp = Component();
      Comp.render();
      currentHook = 0;
      return Comp;
    },
    useState(initialValue) {
      hooks[currentHook] = hooks[currentHook] || initialValue;
      const setStateHookIndex = currentHook;
      const setState = (newState) => (hooks[setStateHookIndex] = newState);
      return [hooks[currentHook++], setState];
    }
  };
})();

function Counter() {
  const [count, setCount] = MyReact.useState(0);
  const [text, setText] = MyReact.useState("foo");
  return {
    click: () => setCount(count + 1),
    type: (txt) => setText(txt),
    noop: () => setCount(count),
    render: () => console.log("render:", { count, text })
  };
}

let App;
App = MyReact.render(Counter);

App.click();
App = MyReact.render(Counter);

App.type("bar");
App = MyReact.render(Counter);

App.noop();
App = MyReact.render(Counter);

App.click();
App = MyReact.render(Counter);

실행 결과는 다음과 같습니다

render: {count0, text"foo"}
render: {count1, text"foo"}
render: {count1, text"bar"}
render: {count1, text"bar"}
render: {count2, text"bar"}
  • setState는 이전 state의 값을 클로져 형태로 저장하고 있습니다
  • setState가 호출되면 MyReact hooks 배열을 갱신합니다
  • hooks의 값들로 useState를 통해 새로운 component를 렌더링합니다

>> 결론적으로, state의 값이 변경되는게 아니라 아예 컴포넌트가 다시 렌더링되면서 함수가 실행되는 것이며, state를 직접 수정하는 것을 방지하기 위해 const로 선언합니다.