home

UI Tree

글 분류
sub
키워드
react
생성일
2023/01/07 13:37
최근 수정일
2023/11/01 23:50
작성중

TLDR;

React는 UI Tree를 그린다. React DOM은 UI Tree와 Browser DOM을 비교하여 바뀐곳을 업데이트 한다. React는 UI Tree상에서 같은 위치에 존재하는 같은 컴포넌트면 해당 컴포넌트 내부 State를 유지한다. state는 JSX 코드 내부에 있는게 아닌 해당 UI Tree상 JSX 코드의 위치와 연결되어 있다.

들어가기 전에

state는 컴포넌트간 격리되어있음
리액트는 어떤 state가 어떤 컴포넌트에 속해있는지 UI tree라는 것을 이용해 추적함
state는 컴포넌트 내부에 종속 되어있는게 아닌(컴포넌트 내부에 살고 있는게 아닌) 리액트 엔진이 추적하는 것
리액트는 JSX로 부터 UI tree들을 만들고 리액트 DOM이 UI tree와 실제 브라우저 돔을 비교하여 바뀐 곳을 업데이트 함.

예시를 이용한 이해

state는 UI tree상 위치에 영향을 받는다

하나의 컴포넌트이지만 UI tree 상에는 서로 다른 위치에 있으므로 서로 분리된, 다른 count를 가지고 있음
리액트는 UI 트리상 같은 위치에 같은 컴포넌트로 존재할 경우 state를 보존함.

UI tree 상에서 없어 질 경우

체크박스를 이용 하여 두번째 컴포넌트를 화면상 표시했다 지웠다 하는게 가능한 로직
사라졌다가 다시 생길 경우 state는 보존되지 않음
UI Tree 상 같은 위치에서 존재하면 리액트는 state를 보존함 → UI tree 상 존재하던 위치에서 사라질 경우 state는 사라짐

UI tree 상 같은 컴포넌트일 경우

UI Tree 상 같은 위치에 존재하고 프로퍼티만 변경될 경우 state는 유지됨
리액트의 관점에선 같은 위치에 같은 컴포넌트

JSX 마크업 상의 위치가 아닌 UI Tree 내의 위치

React가 보는 것은 UI Tree 내의 위치 - JSX 마크업 또는 HTML의 위치가 아님, 말그대로 컴포넌트의 위치(state를 가지고 있는 것은 컴포넌트이므로)
말그대로 couter 컴포넌트가 렌더링되는 부모 + 위치를 확인하여 제랜더링 여부를 확인 할 뿐
주소의 개념으로 받아들이면 쉬움

다른 컴포넌트를 같은 위치에 조건부 렌더링

같은 위치에 다른 컴포넌트를 렌더링할 경우 기존에 존재하던 counter 컴포넌트는 UI Tree 상에서 사라지고 즉, state는 말소되고 새로운 p 태그가 렌더링 된다.
{/* 생략 */} {isFancy ? ( <div> <Counter isFancy={true} /> </div> ) : ( <section> <Counter isFancy={false} /> </section> )} {/* 생략 */}
JavaScript
복사
해당 컴포넌트의 부모 태그가 바뀔 경우 모든 하위 UI Tree를 재구성함
<div><Counter/></div>와 <section><Coutner/></section>은 UI Tree 상 다른 형태이므로 state를 말소시키고 새로 리렌더링함.
부모가 다르면 리액트는 더이상 확인하지 않고 바로 리렌더링함.
state를 유지하고 싶다면 구조 자체가 같아야함.

UI tree 상 같은 위치 같은 컴포넌트 다른 내부 구조

사라지는게 아니라 같은 hierachy에서 순서가 바뀔 경우 state는 유지된다.
컴포넌트가 같은 컴포넌트일 경우 위치가 바뀌더라도 내부 state의 위치는 그대로 유지됨 - key가 없을 경우
해당 코드의 경우 전혀 다른 값이 유지되어야함에도 불구하고 내부 state는 그대로 유지됨
export default function App() { const [reverse, setReverse] = useState(false); let checkbox = ( <label> <input type="checkbox" checked={reverse} onChange={e => setReverse(e.target.checked)} /> Reverse order </label> ); if (reverse) { return ( <> <Field label="Last name" /> <Field label="First name" /> {checkbox} </> ); } else { return ( <> <Field label="First name" /> <Field label="Last name" /> {checkbox} </> ); } }
JavaScript
복사
key를 사용 할 경우 내부 state의 위치도 따라서 같이 변경 될 수 있음 - key를 이용해 리액트에게 서로다른 컴포넌트란걸 알려줄 경우 내부 state또한 함께 추적이 가능
export default function App() { const [reverse, setReverse] = useState(false); let checkbox = ( <label> <input type="checkbox" checked={reverse} onChange={e => setReverse(e.target.checked)} /> Reverse order </label> ); if (reverse) { return ( <> <Field key="lastName" label="Last name" /> <Field key="firstName" label="First name" /> {checkbox} </> ); } else { return ( <> <Field key="firstName" label="First name" /> <Field key="lastName" label="Last name" /> {checkbox} </> ); } }
JavaScript
복사

UI Tree 상 같은 위치에있는 컴포넌트의 state 유지

문제점

export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA ? ( <Counter person="Taylor" /> ) : ( <Counter person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); }
JavaScript
복사
의도한 결과 - Counter 컴포넌트의 property가 바뀔 때 Counter 컴포넌트 내부에 state가 리렌더링 되는걸 의도함
실제 결과 - 해당 컴포넌트는 UI Tree 상 같은 위치에 존재하므로 props가 바뀌더라도 state가 유지됨

해결법 1 - 다른 위치에 렌더링

export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); }
JavaScript
복사
삼항 연산자가 아닌 Logical AND(&&)를 이용해 UI Tree상 다른위치에 렌더링 되도록 함
UI Tree상 다른 위치이므로 내부 state가 유지되지 않음

해결법 2 - 키(key) 활용

export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA ? ( <Counter key="Taylor" person="Taylor" /> ) : ( <Counter key="Sarah" person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); }
JavaScript
복사
좀더 포괄적인 해결 방법
key는 리스트에만 사용하도록 만들어진게 아님
리액트가 구분하도록 어떤 컴포넌트 에서든 사용할 수 있음
key가 전달될 경우 리액트는 UI tree상에서 해당 key로 새로운 컴포넌트인지 아닌지 비교함 → 유니크한 값이 입력되어야하는 이유
다른 key가 전달될 경우 같은 컴포넌튿임에도 불구하고 리액트는 전혀 다른 컴포넌트로 보고 UI Tree 업데이트 → state 말소
Key는 프로젝트 전체에서 유일 무이 해야하는 값이 아님! → 같은 부모 아래에서만 비교하는 값

참조