React hooks는 어떻게 동작하는가? (useState)와 클로저

gun·2021년 4월 28일
3

TIL

목록 보기
19/19
post-thumbnail

useState란?

React hook중 useState를 다들 한번쯤은 사용해 봤을 것입니다.

분명 const로 count와 setCount를 선언 했기에 재 할당이 되지 않는데 어떻게 값을 바꿀수 있는것인가? 내부적으로 어떻게 동작하는거지? 라는 궁금중이 생겼습니다.

const [count, setCount] = React.useState(0);

커스텀 useState

다음과 같이 커스텀 useState를 만들어 보겠습니다.

function useState(initialValue) {
	
  let value = initialValue;  //지역변수 value
    
  function state() {  //value값을 리턴
    	return value;
    }
    
  function setState(newValue) {
   	  value = newValue;	// value값을 재 할당 해준다.
    }
    
  return[state, setState]; 
}

const [count, setCount] = useState(0);
console.log(count()); //0
setCount(3);	// 새로운 값 할당
console.log(count()); //3

다음과 같이 useState의 기본적인 형태의 복사본을 만들었습니다.

위 함수를 이해하기 위해서는 클로저의 개념이 필요합니다.
useState 함수내에 지역변수 value가 선언, 할당 되어있고, state 함수는 value를 리턴해주게 되고, setState는 외부 함수의 value에 인자로 들어온 newValue를 재 할당 해주게 됩니다.

const [count, setCount] = useState(0);

useState 함수를 실행하게 되면 내부에 있는 state와 setState는 클로저의 특징인 외부 함수의 value 값을 참조하고 있는 형태게 됩니다.
그래서 setCount 함수를 실행 했을 때 value값이 업데이트되어 count함수 내부의 value가 업데이트 된 값을 받아볼 수 있는것을 확인할 수 있습니다.

여기서 의문점이 그냥 value를 내보내주면 setState가 업데이트 될 때 변경할 수 있지 않을까? 라는 생각이 들었습니다. state를 없애고 value만 return해주니 다음과 같은 결과가 나왔습니다.

function useState(initialValue) {
	
  let value = initialValue;  //지역변수 value
    
  function setState(newValue) {
   	  value = newValue;	// value값을 재 할당 해준다.
    }
    
  return[value, setState]; 
}

const [count, setCount] = useState(0);
console.log(count()); //0
setCount(3);	// 새로운 값 할당
console.log(count()); //0

왜 이런것일까.. 이유는 간단합니다. value는 그냥 원시값이기 때문. 원시값을 받아와 할당 해주는것이기 때문에 useState의 value와는 전혀 상관이 없는 값이 돼 버렸기 때문입니다.
배열이나 객체를 넣어줬을 때는 문제가 없겠죠?? 한번 직접 해보시길 바랍니다!

hook을 흉내 냈을 뿐 react의 hook과는 매우 달라 보입니다. 조금더 수정을 해주겠습니다.

const MyReact = (function() {
  let _val // 모듈 스코프 안에 state를 선업합니다.
  return {
    render(Component) {	//render함수를 호출할 때마다 Component를 재 생성 합니다.
      const Comp = Component()
      Comp.render()
      return Comp
    },
    useState(initialValue) {
      _val = _val || initialValue // val가 있으면 val값을 업데이트하고, 없다면 initialValue값을 넣어줍니다.
      function setState(newVal) {	//이전과 같습니다.
        _val = newVal
      }
      return [_val, setState]
    },
  }
})()

function Counter() {
  const [count, setCount] = MyReact.useState(1)
  return {
    click: () => setCount(count + 1),
    render: () => console.log(count),
  }
}
let App
App = MyReact.render(Counter) // 1
App.click()
App = MyReact.render(Counter) // 2

이제야 좀 React hooks 와 비슷해 졌습니다.

위 함수에 설명을 하자면, MyReact 함수는 지역 변수로 val을 갖고 있습니다.

render함수는 state의 값이 변하면 리 렌더링 해주는 React 컴포넌트로 보시면 됩니다.

useState가 조금 변경 되었는데 _val 의 값이 비어 있으면 initialValue를 /
_val이 비어있지 않으면 _val 값을 넣어줍니다.

왜 _val을 넣어주는걸까요?

React는 기본적으로 state가 업데이트 되면 컴포넌트를 리 렌더링 하게 되어있습니다.

컴포넌트를 리 렌더링 한다는 것은 즉 컴포넌트를 다시 한번 실행 시킨다는 것으로, 내부에 있는 변수들이 재 선언이 이뤄진다는 것을 예상할 수 있습니다.

이때, const [count, setCount] = MyReact.useState(1) 이 부분이 재 선언이 되어, MyReact.useState의 인자로 (1)이 들어가게 되어 _val값을 확인하는 이유 입니다.


click함수를 여러번 실행 시킨뒤 MyReact.render를 해보시면 어떤 결과가 나오나요?

click함수를 10번 시키든 100번 시키던 render 함수를 실행시키지 않으면 값이 업데이트 되지 않습니다.

App = MyReact.render(Counter) // 1
App.click()
App.click()
App.click()
App.click()
App.click()
App.click()
App = MyReact.render(Counter) // 2

다음과 같은 결과가 나오는 이유는 리렌더링이 이뤄지지 않았기 때문입니다.
위 함수는 click함수 실행시 count+1이 업데이트 되어 저장되지만, Counter컴포넌트가 리렌더링이 발생하지 않아, count는 그대로 1이기 때문에 마지막 render 함수 호출시 2가 출력되게 되는것 입니다.


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

0개의 댓글