본문 바로가기
개발/React

useState와 클로저

by 안뇽! 2022. 11. 23.
반응형

스터디에서 리액트 공식문서로 공부를 하고 있다.

어떤 분이 함수형리액트, 특히 useState를 이해하기 위해서는 클로저를 꼭 이해해야 한다는 말씀을 하셨다.

그말은 내가 useState,useEffect 등의 훅을 사용할 때 원리를 모른채, 사용 설명서만 외워두고 사용하고 있다는 것 을 상기시켜주었다.

저번주에 스터디를 마무리를 하면서 클로저가 어떻게 함수형리액트에 사용되는지 알아보자고 이야기를 했고 모든분들이 찬성해주셨다.

3일동안 한글자 한글자 천천히 읽었는데, 조금 이해가 가는 것 같다. 하지만 느낌만 알 뿐, 유창하게 설명할 정도는 아니다.

 

클로저

클로저 , 어렵다.

내가 내린 정의는 다음과 같다.(사실 구글링짬뽕)

함수가 생성되었을때 접근가능한 스코프를 기억하고, 전혀 다른 환경에서 실행될 때에도 여전히 그 스코프에 대한 참조를 유지할 수 있는 기능

=> 함수 실행이 종료되어도 생성된 시점에 접근 가능했던 모든 것에 계속 접근할 수 있게 된다.

 

다음 코드를 보자.

const handleNum = () => {
    let num = 0;
    const addNum = ()=>{num+=1;console.log(num)}
    const minusNum = () => {num-=1;console.log(num)}
    return [addNum,minusNum]
}

//이때 addNum과 minusNum이 생성되었을때의 환경, let num = 0; 을 기억한다.
const [plus,minus] = handleNum();

plus() //1
plus() //2
plus() //3
minus() //2

콘솔창에 console.log(num) 을 찍어보면 정의되지 않았다고 한다.

 

하지만 plus(),minus() 를 찍어보면 addNum(), minusNum() 이 생성되었을때 접근 가능했던 num에 대한 참조를 유지하고, 계속 num에 +1, -1 을 하고 있음을 알 수 있다.

이렇게 클로저를 통해 함수는 자신이 생성되었을때의 환경을 기억하고 참조를 유지한다는 것을 알 수 있다.

 

또한 위에서 console.log(num)을 찍었을 때 ReferenceError가 나타나는 것으로 클로저는 변수를 은닉함을 알 수 있다.

 

“Closure makes it possible for a function to have ‘private’ variables.”

 

즉, 클로저를 통해 함수의 private한 변수를 생성하고 관리하고 해당 함수를 호출할 때 마다 이 private한 변수에 접근할 수 있다.

 

useState에서 클로저

리액트는 이전 state와 현재 state를 비교하여, 리렌더링 여부를 결정한다.

 

이때 이전 state를 기억하기 위해 클로저를 사용한다.

 

클로저 : 함수가 생성되었을때 접근 가능했던 변수에 대한 참조를 기억하고 유지

 

위에서 handleNum() 코드를 이해했다면 이 코드가 변수이름만 다른 코드라고 느껴질 것이다.

아마 설명을 읽을 필요가 없을 듯. (출처)

 

//import {useState} from 'react'

const React = (function() {
  let _val;
  
  function useState(initVal) {
    const state = _val || initVal;
    const setState = newVal => {
      _val = newVal;
    };

    return [state, setState];
  }
  
  function render(Component) {
    const C = Component();
    C.render();
    return C;
  }
  
  return { useState, render };
})();

function Component() {
  //[count,setCount]가 정의될 때,  
  //[state,setState]가 생성될때의 환경, let _val = 0 을 기억한다.
  const [count, setCount] = React.useState(1);
  
  return {
    render: () => console.log(count),
    click: () => setCount(count + 1),
  }
}

var App = React.render(Component);
App.click(); // 1
var App = React.render(Component);
App.click(); // 2
var App = React.render(Component);
App.click(); // 3

React함수 내부에서 state,setState가 생성될 때, 이 함수들이 접근 가능했던 환경, let _val = 0에 대한 참조를 기억할 수 있다.

또한 _val을 private하게 관리할 수 있기에, 각 컴포넌트만의 상태값을 각각 관리할 수 있다.

const React = (function() {
  let _val;
  
  function useState(initVal) {
    const state = _val || initVal;
    const setState = newVal => {
      _val = newVal;
    };

    return [state, setState];
  }
  
 ...(생략)...
})();

function Component() {
  //[count,setCount]가 정의될 때,  
  //[state,setState]가 생성될때의 환경, let _val = 0 을 기억한다.
  const [count, setCount] = React.useState(1);

 


참고문서

유레카를 주었던 글

 

자바스크립트 클로저로 Hooks구현하기

JSConf.Asia 2019에서 Shawn Wang이 발표한 내용을 정리해보았습니다.

medium.com

 

반응형