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
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
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);
- useState에 의해 _val가 참조되고 있기에 _val는 바뀐값 유지 (여기까지는 Before와 같다.)
```
const useState = (initValue) => {
let _val = initValue;
const state = _val;
const setState = (newValue) => {
_val = newValue;
}
return [state, setState];
}
```
- Before의 경우, useState가 재호출되면서 _val도 재선언되면서 initValue가 다시 들어가게 되므로, 계속 똑같은 값을 가져온다.
- 따라서, 이전 scope를 유지하도록 _val를 외부 scope로 뺴주는 것이 포인트!
...
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를 유지할 수 있다!
Author And Source
이 문제에 관하여(01_React useState와 closure), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@holics1367/01React-useState와-closure저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)