setState의 비동기적 특성과 Dependency


🐤 setState의 특성

setState는 비동기적으로 동작한다.
그리고 state의 업데이트를 매 setState마다 수행하지 않고, 여러 요청이 들어올 때 한 번의 취합과정을 통해 단 한 번 수행한다.

이는 state가 변경될 때마다 리렌더링되는 React의 특성 때문이다. setState가 거의 동시에 4~5개 발생했을 경우 다수의 리렌더링을 방치하는 것보다, 한 번의 업데이트를 통해 한 번의 리렌더링이 될 수 있도록 설계되어 있기 때문이다.

즉, React는 16ms 텀을 기준으로 state를 일괄 업데이트를 진행하여, 렌더링이 한 번만 될 수 있도록 하여 효율성을 증진시킨다.

우선, nodemodules에 접근해 react의 소스코드를 한번 확인해봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// nodemodules/react/umd/react.development.js

Component.prototype.setState = function (partialState, callback) {
if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
{
throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables." );
}
}
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

/// nodemodules/react/index.d.ts

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

위에서 보면 알 수 있듯이, setState의 매개변수에는 partialStatecallback이 들어간다. 그리고 partialState에는 objectfunction 타입이 들어갈 수 있는 것으로 보인다. 그리고 이후에 setState가 발동될 때마다 현재의 partialState와 인자로 넘어온 partialState를 병합해준다.

그리고 useState의 매개변수에는 prevState(S) 또는 함수형(()⇒ S)이 들어갈 수 있다.

따라서 한번에 여러번 state를 업데이트해야 할 경우, 그리고 동기적으로 setState를 사용하고 싶다면 setCount(prev ⇒ prev+1)과 같이 setState 내부에 함수형으로 선언을 해줘야 한다.


🐛 useCallback에서의 dependency 결여로 인한 문제점

이번 프로젝트에서는 <input>value state가 변경될 때마다 API호출이 일어나야 했고, 해당 valuereq에 담아 Get요청을 보내야 했다.

하지만, onChange 이벤트가 발생할 때마다 state가 업데이트 되고, 이러한 setState의 과정은 비동기적이고 dependency 처리를 해줘야 했기 때문에, 계속 API request에 담겨지는 inputValue값이 default값인 ‘’ 로만 인식되어 담겨졌다.

이러한 문제가 발생하는 이유가 궁금해 setState의 비동기적 특성과 dependency 등에 대한 궁금증이 생겼고, 차근차근 하나부터 공부해보고자 결심했다.

문제점

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fetcher = useCallback(
async (entry) => {
if (list.length < 1) return;
const target = entry[0];
if (target.isIntersecting) {
setRange((prev) => prev + 15);
const products = await axios.get(`api/productList`, {
params: { length: range, text: inputValue },
});
setResults(products.data.requests);
}
},
[list]
);

위의 코드는 잘 작동된다. 하지만 처음에 작성했던 코드에서는 useCallback dependency에 list를 추가해두지 않았기 때문에 list.length 값을 default값인 0으로만 인식했다.

useCallback의 특성상, 함수의 참조값이 기준없이 초기화되는 것을 방지하기 위해 반드시 dependency를 추가해줘야 한다. 그래야 해당 dependency를 참조하며 해당 값의 변화를 감지하고, 해당 값의 변화가 있을 때에만 콜백함수의 참조값이 초기화 되기 때문이다.

이에 따라, list의 길이가 변경될 때마다 함수의 참조값이 초기화 되어 새로운 list.length의 값을 새로 불러들여 활용할 수 있도록 dependency에 list props를 넣어주며 문제를 해결했다.


setState의 비동기적 특성과 Dependency

https://hoonjoo-park.github.io/react/setStateAsync/

Author

Hoonjoo

Posted on

2022-02-20

Updated on

2022-02-23

Licensed under

Comments