yeon
yeon I am a Korean web programmer with bboy dancing as a hobby. :)

React Component State

React Component State

React Component State

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


State & Props

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



State 일괄처리

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 호출은 일괄처리되며 이전에 있었던 문제가 해결된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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] :




comments powered by Disqus