React Render Phase에서 EffectList 만드는 과정

류지승·2024년 8월 21일

React

목록 보기
7/19

function Todo() {
  const [todos, push] = useState([]);
  return (
    <>
      <Input submit={todo => push(todos => [...todos, todo])} />
      <OrderList list={todos} />
    </>
  );
};

function Input({ submit }) {
  const [value, setValue] = useState("");
  return (
    <>
      <input value={value} onChange={e => setValue(e.target.value)} />
      <button
        onClick={() => {
          submit(value);
          setValue("");
        }}
      >
        submit
      </button>
    </>
  );
}

function OrderList({ list }) {
  return (
    <ol>
      {list.map(item => (
        <li>{item}</li>
      ))}
    </ol>
  );
}

function App() { return <Todo /> };
ReactDOM.render(<App />, container);

input 입력

<input value={value} onChange={e => setValue(e.target.value)} />

-> onChange에 의해 state값이 변경

-> setState가 호출되면 react 내부적으로 dispatchAction이 호출
-> updateQueue에 렌더링해야한다고 push함

-> beginwork를 통해 updateFunctionComponent를 호출
-> 이 때 업데이트된 fiber를 workInProgress에 적용시킴

-> input 탐색 시 useEffect를 호출하기 되므로 EffectTag에 passive가 달음

-> workInProgress Tree를 다 그리면 fiber에서 react Element로 변경하여야 하고, side Effect List를 만들어야함

-> completeWork를 통해 input props가 변경되었다는 사실을 알게됨 ex) value, children

if (returnFiber.firstEffect === null) {
    returnFiber.firstEffect = workInProgress.firstEffect;
}
if (workInProgress.lastEffect !== null) {
    if (returnFiber.lastEffect !== null) {
        returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
    }
    returnFiber.lastEffect = workInProgress.lastEffect;
}
const effectTag = workInProgress.effectTag;
if (effectTag > PerformedWork) {
    if (returnFiber.lastEffect !== null) {
        returnFiber.lastEffect.nextEffect = workInProgress;
    } else {
        returnFiber.firstEffect = workInProgress;
    }
    returnFiber.lastEffect = workInProgress;
}

-> input element를 변경하였고, 이후 commit phase에서 소비해야할 effectList를 만듬
-> 현재 returnFiber = Input / workInProgress(이하 WIP라고 칭함) = input

Input의 firstEffect, lastEffect = null
input의 firstEffect, lastEffect = null

Input의 firstEffect가 null이므로
Input.firstEffect = input.firstEffect 연결하는데 하지만, input의 firstEffect가 null이므로 Input.firstEffect = null

input.lastEffect가 null이므로 분기 패스

input의 effect Tag가 존재하며, Input.lastEffect가 null이므로
Input.firstEffect = input / Input.lastEffect = input

현재까지의 Effect List
Input - input(FirstEffect & lastEffect)


-> returnFiber = Input / WIF = button

현재 Input.firstEffect = input / Input.lastEffect = input

Input.firstEffect = input이므로 첫번째 분기 제외
button.lastEffect = null이므로 두번째 분기 제외

button에는 effectTag가 존재하고, Input.lastEffect = input이므로
input.nextEffect = button을 할당하고
Input.lastEffect = button을 할당

현재까지의 Effect List
Input - input(firstEffect) - button(lastEffect)


-> returnFiber = Todo / WIP = Input

현재 Todo.firstEffect / lastEffect = null
Input.firstEffect = input / Input.lastEffect = button

첫번째 분기 Todo.firstEffect = null이므로
Todo.firstEffect = Input.firstEffect = input으로 할당
두번째 분기 Input.lastEffect가 존재하고, Todo.lastEffect = null이므로 Todo.lastEffect = input.lastEffect = button 할당

정리
Todo - input(firstEffect) - button(lastEffect)
Input - input(firstEffect) - button(lastEffect)

Input은 effect tag를 갖고 있고, Todo.lastEffect = button이므로
Todo.lastEffect.nextEffect = Input을 할당
Todo.lastEffect = Input 할당
즉 Todo - input(firstEffect) - button - Input(lastEffect)

위부터는 effect Tag가 존재하지 않기때문에 지속적으로 firstEffect와 lastEffect만 부모로 옮겨주는 것만 함.

최종 EffectList => input(firstEffect) - button - Input(lastEffect)

profile
성실(誠實)한 사람만이 목표를 성실(成實)한다

0개의 댓글