지난편 마지막 코드에서 주석과 같은 결과를 출력하는 이유는 아래와 같다.
이유: React는 싱글톤이자 클로저다. 때문에, setCount와 setText가 같은 value값을 가리키기 때문이다.
1. 최초 할당시에 _val은 비어있으므로, 각각 count와 text에 1과 'apple'이 할당됨.
2. click을 하면서, setCount는 _val에 2를 저장해버림. => 두 번째 호출 시에 2 2 를 출력함 : Component 안의 count와 text가 _val 하나를 공유해서 발생하는 문제
3. 2와 같은 이유로 peer peer을 출력
해결방법은?
_val을 어레이로 대체해서 값을 저장하고, 각 useState는 해당 값의 인덱스를 기억한다.
const React = (function(){
let hooks = [];
let idx = 0;
function useState(initVal){
const _idx = idx;
const state = hooks[idx] || initVal;
const setState = newVal =>{
hooks[_idx] = newVal;
};
idx++;
return [state,setState];
}
function render(Component){
idx = 0;
const C = Component();
C.render();
return C;
}
return { useState, render };
})();
function Component() {
const [count,setCount] = React.useState(1);
const [text, setText] = React.useState('apple');
return {
render: () => console.log(count, text),
click: () => setCount(count+1),
type: word => setText(word),
};
}
var App = React.render(Component);// 1 'apple'
App.click();
var App = React.render(Component);// 2 'apple'
App.type('peer');
var App = React.render(Component);// 2 'peer'
해결 방법
let hooks = [];
_val 을 hooks 어레이로 대체했다. 이제 우리는 useState를 몇번을 사용하든 새로운 값들을 차곡차곡 저장할 수 있다.
2.
let idx = 0;
저장을 했으면 해당 데이터를 가지러 갈 수 있도록, index값도 챙겨두기 위해 idx를 만든다.
3.
const _idx = idx;
useState를 호출할 당시 React의 idx 값을 기억한다. => hooks에 저장해둔 자신의 값에 접근하기 위해.
4.
const state = hooks[idx] || initVal;
최초에 useState()를 호출하면 hooks[idx]는 undefined이므로 initVal 값을 state에 담는다.
아직까지 hooks는 빈 어레이이다. 하지만 생성된 useState는 state를 통해 값을 저장하고 있으며, 나중에 사용될 _idx값도 가지고 있다.
5.
const setState = newVal =>{
hooks[_idx] = newVal;
};
idx++;
setCount라든가, setText함수가 호출될 경우, 즉 setState가 호출될 때, 그때 비로소 hooks 어레이의 자신의 칸에 값을 저장한다.
자신의 칸은 자신이 가지고 있는 idx를 통해 접근이 가능하다.
위 예제의 경우 count는 0번 text는 1번이다.
idx++는 윗 줄처럼, useState가 여러번 선언될 경우, 다음 녀석은 내 다음 번호를 가져가라고 ++해주는 것이다.
6.
function render(Component){
idx = 0;
//...생략
}
제일 중요한 놈이다.
React.render(Component) 를 호출할 때마다 idx는 0으로 다시 돌려둔다.
계속 증가하는 idx 때문에, state는 hooks 내부에 있는 자신의 것을 가리키지 못한다.
예를 들어
1. React.render(Component) 최초 호출
2. hooks는 빈 어레이, 각 클로저의 idx에는 0, 1 그리고 state에는 1, 'apple' 저장
=> 1, 'apple' 출력
3. App.click()호출
4. count 값인 1을 불러오고 여기에 1을 더한 값인 2를 hooks[0]에 저장 => hooks = [2]
5. React.render(Component)호출
6. idx 는 2,3 => hooks[2], hooks[3]은 없음 (현재 hooks는 4번 상태) => 위 2번 반복
7. render 시에 hooks[2], hooks[3]은 없으므로, 자신의 state 값 출력
=> 1, 'apple' 출력
이와 같은 방법으로 계속해서 콘솔창에 찍히는 값은 1 'apple' 이다.
다음 시간에는 이어서 useEffect를 만들어보자.