01_React useState와 closure

Outline

  • useState는 React의 Hook으로 functional components에서 상태관리를 도와준다. useState함수의 초기값으로 주어진 인수를 closure를 통해 관리하는 것처럼 보이는데 내가 그동안 알고 있던 closure와는 조금 다른 것 같아 정리하고자 한다.


Before

	function useState(init) {
		let _val = init;
		
		function setState(newVal) {
			_val = newVal;
		}
		
		return [_val, setState];
	}
  • 기존 잘못생각하고, 클로저를 정확히 몰랐을 때, 생각했던, useState는 대충 이렇게 생겼겠지 하고 생각한 코드이다.
  • 보면 setState로 새로운 값을 받아 lexical scope인 _val를 사용하고 있기에 _val를 변화시키고 그 값을 받아오는 것처럼 보인다.
  • 하지만, 이 함수의 return 값 [_val, setState]이 문제이다...
  • [state, setState] = [_val, setState]와 같은 식으로 보통 destrucutring을 하게 되는데, _val는 초기에 한번만 가져오고 끝나기 때문에 useState의 lexical scope에서는 변화가 이루어 질지 몰라도, return 값에는 변화가 없다.

값 자체가 아니라 호출하는 방식으로 바꾸면, 매 호출때, 바뀐 값도 가져올 수 있지 않을까?



Solve problem

  • 1번더 depth를 주는 것과 같은 방향으로 바꾸어야 한다.
const React = (function(){
	let _val;
	
	const useState = (initValue) => {
		_val = _val || initValue;
		
		const state = _val;
		const setState = (newValue) => {
			_val = newValue;
		}
		return [state, setState];
	}
	
	const render = (Components) => 
    const C = Component()
    C.render()
    return C
  }}
	return {useState, render};
}) ()

function Component() {
	 const [state, setState] = useState(0);
	 return {
	 render: () => console.log("component render, state=", state),
	 click: () => setState(state + 1);
}

let App = React.render(Component);
App.click();
let App = React.render(Component);

  • IIFE (Immediately Invoked Function Expression)와 같은 형식으로 정의와 동시에 시행시키는 방식으로 useState hook을 정의한다.
  • 바로 시행되기에 React에는 useState함수가 들어있는 Object가 반환된다.
  • App.click()을 통해 setState가 시행되며 _val가 1로 업데이트 된다.
    - useState에 의해 _val가 참조되고 있기에 _val는 바뀐값 유지 (여기까지는 Before와 같다.)
  • React.render(Component) 에 의해 const C = Component()로 함수 컴포넌트가 재시행 된다.
  • 재시행되면서 useState도 다시 불러와 지는데, 이 때, useState의 state 선언문 또한 재시행 되면서, state = _val이 다시 시행된다. 여기서, state에 _val을 다시 할당하기에, before와는 다르게, 바뀐 _val에 대해서도 그대로 가져올 수 있다.
    	```
    		const useState = (initValue) => {
    		let _val = initValue;
    	
    		const state = _val;
    		const setState = (newValue) => {
    			_val = newValue;
    		}
    		return [state, setState];
    	}
    	```
    	- Before의 경우, useState가 재호출되면서 _val도 재선언되면서 initValue가 다시 들어가게 되므로, 계속 똑같은 값을 가져온다.
    	- 따라서, 이전 scope를 유지하도록 _val를 외부 scope로 뺴주는 것이 포인트!
  • C.render => Component.render( ) => console.log(state) 시행


multiple useState

  • useState는 1번만 쓰이지는 않는다. 그러면 모든 useState는 _val를 참조하게 될텐데... ?
  • _val대신 값을 담아둘 수 있는 배열을 선언하고 현재 선언된 useState의 idx를 저장해 둔다.
	...
	 let idx = 0;
	 const useState = (initValue) => {
			const state = _val[idx] || initValue;
			const _idx = idx;
			const setState = (newValue) => {
				_val[_idx] = newValue;
			}
			++idx; // 다음 useState를 위해 pointer를 다음 칸으로 이동시켜 준다.
	 }
 - useState내부의 _idx는 setState에서 참조중이기에, setState에서 _idx는 이전 스코프가 유지되므로, 각 useState는 자신의 상태가 저장된 Idx를 유지할 수 있다! 
 


Reference

UseState Hook과 closure
원본

좋은 웹페이지 즐겨찾기