Q1) setState를 실행했는데 상태값이 왜 바로 바뀌지 않지?
function App(){
const [count,setCount] = useState(0);
const handleClick = () => {
setCount(5);
console.log(count); // 0
setCount(10);
console.log(count); // 0
}
}
Q2) 훅을 if문이나 함수 안에서 호출하면 왜 안되지?
function App(){
if(condition){
const [state,setState] = useState(0);
// [Error]
// React has detected a change in the order of Hooks called.
// This will lead to bugs and errors if not fixed.
// 리액트가 훅 순서의 변경을 감지했습니다.
// 이는 버그와 오류를 발생시킬 수 있습니다.
}
}
Q1) setState를 실행했는데 상태값이 왜 바로 바뀌지 않지?
Q2) 훅을 if문이나 함수 안에서 호출하면 왜 안되지?
=> React Fiber
Q1) setState를 실행하면 상태값이 왜 바로 바뀌지 않지?
Q2) 훅을 if문이나 함수 안에서 호출하면 왜 안되지?
: 렌더링 이전과 이후의 가상 돔을
비교하여
변경사항을 실제 돔에 적용하는 작업
렌더링 :
함수(함수형 컴포넌트)가 호출되어
가상돔이 만들어지는 과정
단점
: 처리 중이던 렌더링이 완료될 때까지
다른 작업을 수행할 수 없는 상태
=> Fiber 재조정자
: React16에서 등장한 재조정 엔진
우선순위가 더 높은 작업을 먼저 처리
상태를 가질 수 없었다.
=> React Hooks
=> Fiber Node가 관리
: 훅을 포함한 컴포넌트의 모든 상태를
저장하는 자바스크립트 객체
: 훅을 포함한 컴포넌트의 모든 상태를
저장하는 자바스크립트 객체
export type Fiber = {
tag: WorkTag; // 컴포넌트 유형 (함수형, 클래스형, 호스트 등)
key: null | string; // React 엘리먼트의 고유 식별자 (주로 리스트에서 사용)
elementType: any; // React.createElement 호출 시 사용된 원래 타입
type: any; // 컴포넌트 정의 (함수형 컴포넌트의 함수,
// 클래스 컴포넌트의 클래스)
stateNode: any; // 호스트 컴포넌트의 DOM 노드나 클래스 컴포넌트의 인스턴스
// ..
};
export type Fiber = {
return: Fiber | null; // 부모 Fiber (이 Fiber를 렌더링한 컴포넌트)
child: Fiber | null; // 첫 번째 자식 Fiber
sibling: Fiber | null; // 다음 형제 Fiber
index: number; // 같은 부모 내에서의 인덱스 (형제 간 순서)
// ..
};
export type Fiber = {
ref: RefObject; // ref 객체 또는 함수
refCleanup: null | (() => void); // ref 정리를 위한 콜백 함수
// ..
};
export type Fiber = {
pendingProps: any; // 아직 처리되지 않은 새 props
memoizedProps: any; // 마지막으로 렌더링된 props
updateQueue: mixed; // 상태 업데이트, 콜백, 사이드 이펙트를 위한 큐
memoizedState: any; // 함수형 컴포넌트의 훅 목록(연결 리스트)을 저장
// 클래스 컴포넌트에서는 state 객체를 저장
dependencies: Dependencies | null; // 컨텍스트, 이벤트 등 외부 의존성
mode: TypeOfMode; // Concurrent, Strict 등의 렌더링 모드
// ..
};
export type Fiber = {
flags: Flags; // 이 Fiber에 적용할 작업 종류
// (배치, 업데이트, 삭제 등)
subtreeFlags: Flags; // 자식 Fiber들에 적용할 작업 종류
deletions: Array | null; // 삭제될 자식 Fiber들의 배열
// ..
};
export type Fiber = {
lanes: Lanes; // 이 Fiber의 작업 우선순위
childLanes: Lanes; // 자식 Fiber들의 작업 우선순위
// ..
};
export type Fiber = {
// ..
alternate: Fiber | null; // 현재 트리와 작업 중인 트리 사이의 대응 Fiber
};
export type Fiber = {
// ..
updateQueue: mixed; // 상태 업데이트, 콜백, 사이드 이펙트를 위한 큐
memoizedState: any; // 함수형 컴포넌트의 훅 목록(연결 리스트)을 저장
// 클래스 컴포넌트에서는 state 객체를 저장
};
const [count,setCount] = useCount(0);
const handleClick = () => {
setCount(count+1);
console.log(count); // 0
setCount(count+1);
console.log(count); // 0
}
: 상태 업데이트를 관리하는 큐
setState를 실행하면 updateQueue에 추가하고,
렌더링
주기가 끝날 때 업데이트를 한 번에 처리
const [count,setCount] = useCount(0);
const handleClick = () => {
setCount(count+1);
console.log(count); // 0
setCount(count+1);
console.log(count); // 0
}
A) Fiber의 updateQueue에 상태 업데이트가 쌓이고
렌더링
주기가 끝날 때 일괄 처리되기 때문
if(condition){
const [state,setState] = useState(0);
// [Error]
// React has detected a change in the order of Hooks called.
// This will lead to bugs and errors if not fixed.
}
function ExampleComponent() {
const [count, setCount] = useState(0); // 1번째 Hook
const [text, setText] = useState('hi'); // 2번째 Hook
useEffect(() => { // 3번째 Hook
document.title = text + count;
}, [text, count]);
// ..
}
Fiber.memoizedState = {
memoizedState: 0, // useState(0)
next: {
memoizedState: 'hi', // useState('hi')
next: {
memoizedState: { deps: ['hi', 0], ... }, // useEffect
next: null
}
}
}
=> 함수형 컴포넌트가 리렌더링되어도 훅의 이전 상태를 기억
if(condition){
const [state,setState] = useState(0);
// [Error]
// React has detected a change in the order of Hooks called.
// This will lead to bugs and errors if not fixed.
}
A) 훅은 Fiber의 memoizedState에 연결 리스트 형태로
저장되어, 순서가 바뀌면 이전 상태와 매칭이
불가능해지기 때문
: 컴포넌트의 props를 관리
가상돔, 컴포넌트 라이프 사이클, 재조정, 동시성
이러한 질문에 대한 답변을 잘하기 위해서 리액트 파이버같은 내부 구조가 빠질 수 없다.
"리액트 파이버란
리액트의 재조정 엔진이자
컴포넌트의 모든 상태를 관리하는
자바스크립트 객체이다."
감사합니다🙇♂️