data life

[React] State 본문

Front-end/React

[React] State

주술회전목마 2023. 3. 29. 01:29

State

 


1️⃣ state를 쓰는 이유

2️⃣ useState()

3️⃣ state 불변성

4️⃣ state 다루기

 

 

1️⃣ state를 쓰는 이유

버튼 클릭시 ,+1 이 되도록 구현하는 카운터로 예시를 들어봄

/**App.js**/
function App(){
   return (
       <div className = "App">
            <Counter />
       </div>
   )
}
export default App;
/**Counter.js**/
export default function Counter(){
   let count = 0;
   return(
      <div>
           <button onClick={()=>(count +=1)}>+1</button>Counter : {count} 
      </div>
  );

➡️카운터 클릭 시, 변경되지 않는 것을 확인할 수 있다. 

왜냐하면, 카운터는 단지 함수구문이기 때문에!
따라서, 함수 컴포넌트 내에서 상태를 관리해야하는 일이 필요하다.

 

🤔컴포넌트가 상태를 관리한다는 의미?

상태값을 변경할 수 있고 변경시 컴포넌트가 랜더링이 일어나게 할 수 있다는 것을 의미한다!
리액트에서는 hook함수라는 것을 통해 상태를 관리하는데
그 중 useState는 배열을 리턴하여 컴포넌트의 리렌더링을 발생시킨다.

 

 

2️⃣ useState()

//state (값), setState(변경 값)
const [state, setState] = useState();
/**Counter.js**/
export default function Counter(){
   const [count, setCount] = useState(0);
   return(
      <div>
           <button onClick={()=>setCount(count+1)}>+1</button>Counter : {count} 
      </div>
  );
}

 

 

3️⃣ state 불변성

 

❓불변성 (immutable) 이란?

"변하지 않는 성질"
: 프로그래밍에서 불변성을 지킨다라는 의미는 메모리 영역 값을 직접적으로 변경하지 않는다는 뜻!

 

setState() => 컴포넌트의 re-rendering

 

만일, 불변성을 지키지 않고, 메모리 영역값을 직접 변경하면 리액트는 state가 변경되었다고 인식하지 못한다.

=> re-rendering을 발생시키지 않게됨

 

왜냐하면 리액트는 이전 state와 이후 state를 비교할 때, 얕은 비교(shallow Compare)를 하기 때문!!

💡얕은 비교
에 대해 알아보자면,
이전, 자바스크립트에서 원시타입과 참조타입이 존재하는 것을 알 수 있었는데 원시타입은 불변성을 지니고 있어 변수에 원시 타입 값을 할당하면 메모리에 값 자체가 저장이 되고
반면, 참조 타입은 불변성을 가지고 있지 않아 변수에 참조 타입 값을 할당하면, 메모리 값이 담긴 주소가 저장이 된다.

 

 

 

4️⃣ state 다루기

 

원시타입의 state 부터 다뤄보도록 하자.

 

  • +1 버튼 : 클릭 시, 값을 1씩 증가시킨다.
  • Show and Hide 버튼 : 클릭 시, 나타나거나 사라지게 한다.
  • Change Operator : + - * 중 랜덤하게 선택하여 카운터 계산에 적용한다.
/**Counter.js**/
export default function Counter(){
   const [count, setCount] = useState(0);
   const [show, setShow] = useState(true);
   const operators = ["+", "-", "*"];
   const [operator, setOperator] = useState(operators[0]);
   
   return(
      <div>
           <button onClick={()=>{
              let result;
              if (operator === "+") result = count + 1;
              if (operator === "-") result = count - 1;
              if (operator === "*") result = count * 1;
              setCount(result);
           }}>{operator}1</button>
           <button onClick={()=>setShow(!show)}>Show and Hide</button>
           <button onClick={()=>{
              const idx = Math.floor(Math.random() * operators.length);
              setOperator(operators[idx]);
           }}>Change Operator</button>
           { show && `Counter : ${count} `}
           
      </div>
  );
}

 

 

이번엔 참조타입의 state를 다뤄보도록 하자.

const [count, setCount] = useState(0);
const [show, setShow] = useState(true);
const [operator, setOperator] = useState(operators[0]);

⬇️

const [info, setInfo] = useState({
   count : 0,
   show : true,
   operator : operators[0];
});

과 같이 object 형태로 변경해주도록 하자

 

   return(
      <div>
           <button onClick={()=>{
              let result;
              if (info.operator === "+") result = count + 1;
              if (info.operator === "-") result = count - 1;
              if (info.operator === "*") result = count * 1;
              setInfo(result);
           }}>{info.operator}1</button>
           <button onClick={()=>{
              info.show = !info.show;
              const newInfo = info;
              setInfo(newInfo);
           }}>Show&Hide</button>
           <button onClick={()=>{
              const idx = Math.floor(Math.random() * operators.length);
              setInfo(info.operators[idx]);
           }}>Change Operator</button>
           { info.show && `Counter : ${info.count} `}        
      </div>
  );
}

객체로 변경된 만큼 아래 코드도 다음과 같이 변경해주었다.

그러나 랜더링이 되지 않는다.😥

<button onClick={()=>{
   info.show = !info.show;
   const newInfo = info;
   setInfo(newInfo);
}}>Show&Hide</button>

앞서 말한 것처럼, 참조타입인 객체가 주소만을 복사했기 때문에 리액트는 객체값이 변경되었다는 것을 확인할 수 없게 된다. 그래서 위와 같은 코드로 작성하면 못 알아채는 것이었음

 

그렇기 때문에 새로운 객체를 setInfo()에 넣어주어야 한다.

=> 스프레드 구문을 이용해줌

<button onClick={()=>{
   setInfo({...info, show : !info.show});
}}>Show&Hide</button>

다른 버튼들도 위와 같이 작성해주도록 하자.

<button onClick={()=>{
  let result;
  if (info.operator === "+") result = count + 1;
  if (info.operator === "-") result = count - 1;
  if (info.operator === "*") result = count * 1;
  setInfo({...info, count : result});
}}>{info.operator}1</button>
<button onClick={()=>{
  const idx = Math.floor(Math.random() * operators.length);
  setInfo(...info, operator : operators[idx]);
}}>Change Operator</button>

 

배열(array)에서도 마찬가지이다.

 

✅잘못된 사용방법

arr[0] = "Hi"
const newArr = arr;
setArr(newArr)

✅올바른 사용방법

setArr([...arr, newItem])
setArr([arr.filter(arr=>{}))

 

'Front-end > React' 카테고리의 다른 글

[React] State 끌어올리기  (0) 2023.03.29
[React] State의 비동기적 업데이트  (0) 2023.03.29
[React] 라이브러리 vs 프레임워크  (0) 2023.03.24
[Project] React로 단위변환기 만들기  (0) 2022.12.25
[React] React 기초  (0) 2022.12.17