vanilla javascript로 과제를 하라고..? 아... 리액트가 그립다... 생각해보니 만들면 되잖아?
vanilla javascript로 과제하기가 불편해서 나만의 프레임워크를 만들어 보고 싶어졌다.
관련 글을 찾아보니 React 관련 글이 많았다. 역시 인기 1위답다.
그래서 나만의 프레임워크는 일단 제쳐두고 React를 만들어보기로 했다.
(react 컨트리뷰터가 되고싶은 야망, react를 알고싶은 마음도 있었다.)
useState를 구현한 게시글은 많았다.
하지만 실제로 사용해보니 문제가 있었다.
함수형 업데이트를 지원하지 않는다는 것
아마 구현만하고 실제 프로젝트를 만드는데 써보진 않았으셨을지도...ㅠㅠ
그래서 이 글에 함수형 업데이트까지 구현한 기록을 담으려고 한다.
useState 구조
useState의 기본 구조는 이렇다.
let state = undefined;
function useState(initialState) {
if (state === undefined) {}
state = initialState;
}
const setState = (newState) => {
state = newState;
render(); // setState 후 render함수 호출
}
return [ state, setState ];
}
하지만 이렇게 구현한다면 state가 여러개일 때 똑같은 값을 보여주게 된다.
그래서 여러개의 상태를 관리할 수 있도록 배열로 저장해준다.
여러 state 보관하는 방법
let currentStateKey = 0;
const states = [];
function useState(initialState) {
if (states.length === currentStateKey) {
states.push(initialState);
}
const state = states[currentStateKey];
const setState = (newState) => {
states[currentStateKey] = newState;
render();
}
currentStateKey += 1;
return [ state, setState ];
}
하지만 아직 문제가 있었다.
이 글에는 없지만, render함수에 렌더링 최적화를 위해 debouce를 적용했다.
그래서 이전 값을 활용해 state를 업데이트 할 때 제대로 업데이트 되지 않는 현상이 발생했다.
해결하려면 함수형 업데이트를 사용하면 된다.
함수형 업데이트 구현
여기서 부터는 다른 글을 참고하지 않고 구현했다.
let currentStateKey = 0;
const states = [];
function useState(initialState) {
if (states.length === currentStateKey) {
states.push(initialState);
}
const state = states[currentStateKey];
const setState = (newState) => {
if(typeof newState === "function") { // setState 인자가 함수라면
states[currentStateKey] = newState(states[currentStateKey]); // 이전 값을 넣어서 함수를 실행해준다
} else {
states[currentStateKey] = newState; 함수가 아니라면 바로 저장한다
}
render();
}
currentStateKey += 1;
return [ state, setState ];
}
setState의 인자가 함수인지 체크한다.
함수라면 이전 값을 넣어서 실행하면 된다.
함수가 아니라면 값을 바로 저장한다.
알게 된 것
useState는 비동기적으로 동작하지만 비동기함수가 아니다. (promise를 반환하지 않는다.)
useState가 비동기적으로 동작하는 이유는 렌더링 최적화 때문이다.
useState가 클로저를 어떻게 사용하고 있는지 알게 되었다.
참고한 글
Vanilla Javascript로 React UseState Hook 만들기