Published on

React Component State

Authors
  • avatar
    Name
    Luffy Yeon
    Twitter

React Component State

React 에서 State는 컴포넌트의 상태 값을 가리킨다. setState 를 통해 상태 값 변경이 가능하고 상태 값이 변경되면 컴포넌트는 리랜더링 된다.


State & Props

State는 컴포넌트의 상태 값으로 컴포넌트 내부에서 관리되는 값이다. Props는 Properties의 줄임말로 함수의 매개변수와 같이 컴포넌트로 전달되는 값이다. State가 하위컴포넌트로 전달되고 하위컴포넌트는 이 값을 Props로 받게 된다.



State 일괄처리

컴포넌트에서 state를 변경하기위해 setState를 사용할 때 setState가 여러 번 호출된다면? 랜더링이 계속 일어나는가?.. 이 답은 아니다. setState에 새롭게 설정할 상태 값인 객체를 전달하게 되는 경우 마지막으로 설정된 State로 랜더링이 된다.

incrementCount() {
// 주의: 이 코드는 예상대로 동작하지 *않을 것*입니다.
this.setState({count: this.state.count + 1});
}
handleSomething() {
// `this.state.count`가 0에서 시작한다고 해봅시다.
this.incrementCount();
this.incrementCount();
this.incrementCount();
// React가 컴포넌트를 리렌더링할 때 `this.state.count`는 3이 될 것 같은 예상과 달리 1이 됩니다.
// 이것은 `incrementCount()` 함수가 `this.state.count`에서 값을 읽어 오는데
// React는 컴포넌트가 리렌더링될 때까지 `this.state.count`를 갱신하지 않기 때문입니다.
// 그러므로 `incrementCount()`는 매번 `this.state.count`의 값을 0으로 읽은 뒤에 이 값을 1로 설정합니다.
// 이 문제의 해결 방법은 아래에 설명되어 있습니다.
}

setState호출할 때마다 리랜더링 되지 않아 this.state 값이 갱신되지 않기 때문에 this.state.count 값은 1이 된다.


위의 문제점 해결은 setState에 함수를 전달하는 방법으로 해결이 가능하다. setState에 전달되는 함수는 이전 상태 값 state에 접근 할 수 있고 setState 호출은 일괄처리되며 이전에 있었던 문제가 해결된다.

incrementCount() {
this.setState((state) => {
// 중요: 값을 업데이트할 때 `this.state` 대신 `state` 값을 읽어옵니다.
return {count: state.count + 1}
});
}
handleSomething() {
// `this.state.count`가 0에서 시작한다고 해봅시다.
this.incrementCount();
this.incrementCount();
this.incrementCount();
// 지금 `this.state.count` 값을 읽어 보면 이 값은 여전히 0일 것입니다.
// 하지만 React가 컴포넌트를 리렌더링하게 되면 이 값은 3이 됩니다.
}

setState에 partialState 인자가 object, function인 경우 updater.enqueueSetState로 설정한다.


react/src/ReactBaseClasses.js

Component.prototype.setState = function (partialState, callback) {
invariant(
typeof partialState === 'object' || typeof partialState === 'function' || partialState == null,
'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')
}

classComponentUpdater를 거쳐 enqueueUpdate로 update 정보가 설정


react-reconciler/src/ReactFiberClassComponenet.js

const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
...
},

payload가 function인 경우 이전 state로 설정하여 호출하는 것을 볼 수 있다.


react-reconciler/src/ReactUpdateQueue.js

function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
// Updater function
if (__DEV__) {
enterDisallowedContextReadInDEV();
}
const nextState = payload.call(instance, prevState, nextProps);
...
}

위의 예시는 React Class Component 인 상황에 해당되며 Functional Component HookuseState의 경우에는 ReactFiberHooks.js를 참고하여야 한다.



[Ref] :