본문 바로가기
목차
[노트장] 적으며 정리해 보는 이론/리액트문서

[리액트공식문서] State를 보존하고 초기화하기

by 졸린부엉이 2024. 4. 3.

학습내용

  • React가 언제 state를 보존하고 언제 초기화하는지
  • 어떻게 React가 컴포넌트의 state를 초기화하도록 강제할 수 있는지
  • key와 타입이 state 보존에 어떻게 영향을 주는지

 

 State 렌더트리에 위치를 바라봅니다  

 

React는 컴포넌트가 UI트리에서

그 자리에 렌더링되는 한 state를 유지합니다.

 

하지만, UI트리에서

그 자리에 다른 컴포넌트가 랜더링 되거나 제거를 하게되면 그 state를 제거합니다.

 

 

 

 

 

React는 트리의 동일한 컴포넌트를 동일한 위치에 렌더링하는 동안 상태를 유지합니다.

이 둘은 각각 트리에서 자기 고유의 위치에 렌더링되어 있으므로 분리되어있는 카운터입니다. 

카운터 버튼을 클릭시 해당 컴포넌트만 갱신되는 것을 확인 할수 있습니다.

 

 

 

[중요] 두번째 counter를 show, hide를 하면서 보여주고, 안 보여주면 state는 어떻게 될까?

 

두번째 counter를 hide때, React는 컴포넌트를 제거하며 동시에 state도 같이 제거합니다.

그래서 counter가 '1'일때 조건부 랜더링을 하여, 숨겼다 보이게 하면 초기값인 '0'으로 값이 변경되어 있습니다.

 

React는 컴포넌트가 UI트리에서

그 자리에 렌더링되는 한 state를 유지합니다.

 

하지만, UI트리에서

그 자리에 다른 컴포넌트가 랜더링 되거나 제거를 하게되면 그 state를 제거합니다.

export default function App() {
  const [showB, setShowB] = useState(true);
  return (
    <div>
      <Counter /> // 첫번째 자식
      {showB && <Counter />} // 두번째 자식 show || hide 
      <button onClick={e => {setShowB((prev)=>!prev)}}>
        toggle button
      </button>
    </div>
  );
}

 

 

 


 

 

[중요] 같은 자리에 같은 컴포넌트의 state는 어떻게 될까?

 

아래 코드는 같은자리(첫번째 자식)에서 조건부 랜더링으로 show, hide를 하고있는 상태입니다.

UI트리상 같은 자리 이기에 2개의 컴포넌트를 번갈아 가며, 보이고 안보이고를 하여도 값에 대한 변화를 바뀌지 않으며,

보존합니다.

export default function App() {
const [isFancy, setIsFancy] = useState(false);
  return (
    <div>
      {isFancy ? (
        <Counter isFancy={true} /> // 첫번째 자식
      ) : (
        <Counter isFancy={false} /> // 첫번째 자식
      )}
      <button onClick={e => {setIsFancy((prev)=>!prev)}}>
        toggle button
      </button>
    </div>
  );
}

 

 


 

[중요] return문 자체를 조건부 랜더링을 한다면?

React는 JSX 마크업에서가 아닌 UI 트리에서의 위치에 관심이 있다는 것을 기억해야 합니다.

React는 UI트리만을 봅기 때문에 Root에서 같은 선상이기에 값은 변화하지 않으며 보존합니다.

 

export default function App() {
const [isFancy, setIsFancy] = useState(false);

if (isFancy) {
    return ( // true
          <div>
            <Counter isFancy={true} /> // 첫번째 자식
            <button onClick={e => {setIsFancy((prev)=>!prev)}}>
            toggle button
            </button>
        </div>
    )}
    
    return (  // false
        <div>
            <Counter isFancy={false} /> // 첫번째 자식
            <button onClick={e => {setIsFancy((prev)=>!prev)}}>
            toggle button
            </button>
        </div>
    )

}

 

 

 


 

 

 

state 유지 경우  

 

같은 위치에 같은 컴포넌트의 각각의 state 유지하기 위해선 끌어올리기를 하여 state를 유지 합니다.

import { useState } from 'react';

export default function Scoreboard() {
  const [isPlayerA, setIsPlayerA] = useState(true);
  const [score, setScore] = useState(0);
  const [score1, setScore1] = useState(0);
  return (
    <div>
      {isPlayerA ? (
        <Counter person="Taylor" score={score} setScore={setScore} />
      ) : (
        <Counter person="Sarah" score={score1} setScore={setScore1}/>
      )}
      <button onClick={() => {
        setIsPlayerA(!isPlayerA);
      }}>
        Next player!
      </button>
    </div>
  );
}

function Counter({ person, score, setScore }) {
  let className = 'counter';

  return (
    <div
      className={className}
    >
      <h1>{person}'s score: {score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}

 

 

 

 

유지되는 경우

state를 유지하는 몇가지 방법이 있다

1. CSS로 안 보이게 할 수 있습니다. 채팅은 트리에서 사라지지 않을 것이고 따라서 그들의 state는 유지됩니다.
이 방법은 간단한 UI에서 잘 작동합니다. 하지만 숨겨진 트리가 크고 많은 DOM 노드를 가지고 있다면 매우 느려질 것입니다.

2. state를 상위로 올리고 각 수신자의 임시 메시지를 부모 컴포넌트에 가지고 있을 수 있습니다.
이 방법에서 부모가 중요한 정보를 가지고 있기 때문에 자식 컴포넌트가 제거되어도 상관이 없습니다. 이것이 가장 일반적인 해법입니다.

3. React state 이외의 다른 저장소를 이용할 수도 있습니다. 예를 들어 유저가 페이지를 실수로 닫아도 메시지를 유지하고 싶을 수도 있습니다. 이때 localStorage에 메시지를 저장하고 이를 이용해 Chat 컴포넌트를 초기화할 수 있습니다.
// 보존되는 경우

const [isFancy, setisFancy] = useState(true);

1.css로 숨기고, 보이고를 하면 state 유지가 가능하다
{isFancy ? (
	<Counter isFancy={true} className={isFancy ? show : hide}/> 
) : (
	<Counter isFancy={false}  className={isFancy ? hide : show}/> 
)}

2. 같은 위치에 같은 컴포넌트의 경우 state 유지한다
{isFancy ? (
	<Counter isFancy={true} /> 
) : (
	<Counter isFancy={false} /> 
)}

3. 부모에서 자식 state 관리한다
import { useState } from 'react';

export default function App() {
  const [isFancy, setIsFancy] = useState(false);
  return (
    <div>
      <Counter isFancy={isFancy} /> // 컴포넌트 유지되며, 안의 내용이 바뀜
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        Use fancy styling
      </label>
    </div>
  );
}

function Counter({ isFancy }) {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);
  
  let className = 'counter';


  if (isFancy) return <p>유지 됩니다.</p> // show, hide
  
  return ( // show, hide
    <div
      className={className}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}

 

 

 

 


 

 

초기화 경우  

state 초기화를 하기 위한 방법은 입니다.

  1. 다른 위치에 컴포넌트를 렌더링하기
  2. 각 컴포넌트에 key로 명시적인 식별자를 제공하기
1.다른 컴포넌트
{isPlayerA &&
	<Counter person="Taylor" />
}
{!isPlayerA &&
	<Counter person="Sarah" />
}


2.key값 부여
{isPlayerA ? (
	<Counter key="Taylor" person="Taylor" />
) : (
	<Counter key="Sarah" person="Sarah" />
)}

 

 

초기화 되는 경우 추가

// 제거되는 경우

const [showB, setShowB] = useState(true);


1. 컴포넌트를 제거 했다가 보이게 했을때도, state값을 제거 됩니다.
<Counter />
{isFancy && <Counter />} 


2. 같은 위치에 다른 컴포넌트때도, state 값은 제거 됩니다.
{isFancy ? (
    <p>See you later!</p> 
    ) : (
    <Counter /> 
)}

or

{isPlayerA &&
	<Counter person="Taylor" />
}
{!isPlayerA &&
    <Counter person="Sarah" />
}

or

{isPlayerA ? <Counter person="Taylor" : null}
{isPlayerA ? undefined : <Counter person="Sarah" }
	

3. <Counter> 컴포넌트는 같지만, 부모의 태그가 다를때 state는 제거 됩니다.
{isFancy ? (
    <div>
      <Counter isFancy={true} /> 
    </div>
    ) : (
    <section>
      <Counter isFancy={false} />
    </section>
)}

 

 


 

 요약 

 

 

  • React는 같은 컴포넌트가 같은 자리에 렌더링되는 한 state를 유지합니다.
  • state는 JSX 태그에 저장되지 않습니다. state는 JSX으로 만든 트리 위치와 연관됩니다.
  • 컴포넌트에 다른 key를 주어서 그 하위 트리를 초기화하도록 강제할 수 있습니다.
  • 중첩해서 컴포넌트를 정의하면 원치 않게 state가 초기화될 수 있기 때문에 그렇게 하지 마세요.

 

 

 

 

 

[참고]

https://ko.react.dev/learn/preserving-and-resetting-state

https://www.youtube.com/watch?v=h6BpLB6ZVrw&list=PLjQV3hketAJkh6BEl0n4PDS_2fBd0cS9v&index=25