240811 React state

신한별·2024년 8월 11일

💪 매일 기록

목록 보기
50/97
post-thumbnail

01. State

1. State 란?

State란 컴포넌트 내부에서 바뀔 수 있는 값을 의미합니다.
(UI(엘리먼트)로의 반영을 위해서)

const name = "훈이"
이 값이 바뀌어야하는 정보라면 state로 생성

2. State 만들기

State를 만들 때는 useState()를 사용 (훅)


import React, { useState } from 'react';

function GrandFather() {
  const [name, setName] = useState("신형만"); // state! (여기서 "신형만"은 초기값)
  return <Mother grandFatherName={name} />;
}

// .. 중략 

useState 훅(기능)을 사용하는 방식

const [ value, setValue ] = useState( 초기값 );
배열의 구조분해 할당 사용

const [name, setName] = useState("신형만");
  1. const 로 선언을 하고 [ ] 빈 배열 을 생성
  2. 배열의 첫번째 자리에는 이 state의 이름,
  3. 두번째 자리에는 set 을 붙이고 state의 이름을 붙임
  4. useState( ) 의 인자에는 이 state의 원하는 초기값(initial state)을 넣어줌
// src/App.js
import React, { useState } from "react";

function Child(props) {
  return <div>{props.grandFatherName}</div>;
}

function Mother(props) {
  return <Child grandFatherName={props.grandFatherName} />;
}

function GrandFather() {
  const [name, setName] = useState("신형만"); // state를 생성
  return <Mother grandFatherName={name} />;
}

function App() {
  return <GrandFather />;
}

export default App;

3. State 변경하기

state를 변경할때는 setValue(바꾸고 싶은 값)

  1. Child 컴포넌트에 할아버지 이름 바꾸기 라는 라벨을 가진 버튼 만들기
  2. 이 버튼을 눌렀을 때 setName 을 실행 할 수 있도록 준비
  3. GrandFather에 있는 setName을 Child에게 전달을 해줘야 함. 주고 받고, 주고 받고해서 전달
  4. 이렇게 바뀐 값은 브라우저를 새로고침하면 다시 초기값으로 바뀜. setName을 통해서 바꾼 값은 어디에 저장되는 것이 아니기 때문에 단순히 화면에서만 바뀐 값으로 다시 렌더링이 되는 것.

02. State 기본응용

1. useState + onClick event

button과 이벤트 핸들러 구현하기 & state 구현하고 이벤트 핸들러과 연결

import React, { useState } from "react";

function App() {
  const [name, setName] = useState("훈이");

  function onClickHandler() {
    setName("울보자식");
  }

  return (
    <div>
      {name}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;
  • 버튼을 눌렀을 때 하고 싶은 행동을 함수로 만듦
    onClickHandler 라는 함수를 만들고 onClick={} 에 넣어줌
    💛 React에서는 이러한 방식으로 함수와 컴포넌트(button 태그)를 연결 시킴 => 이벤트 핸들러
  • 버튼을 누르면 훈이가 울보자식으로 바뀐다.

2. useState + onChange event

(1) input과 state 구현하기

input에서는 보통 사용자가 입력한 값을 state로 관리하는 패턴을 많이 사용

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  return (
    <div>
      <input type="text" />
    </div>
  );
};

export default App; 
  • input과 useState를 사용해서 input의 값을 넣을 value라는 state를 생성
    (function keyword를 사용하지 않고 화살표 함수를 사용한 예시)

(2) 이벤트 핸들러를 구현하고 state와 연결하기

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
  };

	console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={value} />
    </div>
  );
};

export default App;

input에 onChange라는 이벤트를 불러내고, 생성한 이벤트 핸들러 함수를 넣어줌

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
  };

	console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={value} />
    </div>
  );
};

export default App;
  1. 이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용
  2. 사용자가 입력한 input의 값은 event.target.value 로 꺼내 사용
  3. 마지막으로 state인 value를 input의 attribute인 value에 넣어주면 input과 state 연결하기 끝!

💡 꼭 onChange와 value를 둘 다 연결해야만 하나?(제어 컴포넌트의 개념)

React에서는 input 요소의 value 속성과 onChange 이벤트를 함께 사용하여 입력 필드의 상태를 관리하는 것이 일반적.
이러한 패턴을 제어 컴포넌트(Controlled Component)라고 한다.

( React에 의해 값이 제어되는 컴포넌트가 제어 컴포넌트
= state에 의해 값이 제어되는 input 컴포넌트는 제어 컴포넌트 )

제어 컴포넌트에서는 React의 상태가 소스 오브 트루스(source of truth) 역할을 하며, 이를 통해 입력 필드의 현재 값이 항상 React 컴포넌트의 상태와 동기화된다.

source of truth? 어떤 정보의 정확하고 최신의 상태를 유지하는 주된 위치 또는 저장소.
위의 예시에서 리액트의 state이 input의 value와 엮여있으므로 state 자체가 input의 변경된 정보 자체를 가리킴
데이터의 일관성, 정확성을 유지하기 위해 필수!

💡 비제어 컴포넌트로 관리하면 안되나? (value를 연결하기 싫다)

useState를 사용하면서 input 요소의 value 속성에 상태를 연결하지 않는 경우, 이는 비제어 컴포넌트와 유사한 동작을 하게 된다.
이렇게 구현하면 input 요소는 자체적으로 내부 상태를 유지하게 되고, React의 상태는 입력값과 동기화되지 않는다.

다만, onChange 이벤트를 통해 입력값의 변경을 감지하고 React의 상태를 업데이트할 수는 있다.

이렇게 하면 사용자 입력에 대해 반응할 수 있지만, 입력 필드의 값은 React 상태에 의해 제어되지 않는다.
이 방식은 입력 필드가 React의 상태를 기반으로 렌더링되지 않는다.

03. 불변성

1. 불변성이란? (+메모리에 저장되는 원리)

(1) 불변성

불변성 : 메모리에 있는 값을 변경할 수 없는 것

자바스크립트의 데이터 형태중에 원시 데이터는 불변성 O
원시 데이터가 아닌 객체, 배열, 함수는 불변성 X

(2) 변수는 메모리에 어떤식으로 저장되나?

A. 원시 데이터의 경우

let number = 1 선언을 하면 메모리에는 1 이라는 값이 저장 => number 라는 변수는 메모리에 있는 1을 참조
let secondNumber = 1 다른 변수를 선언을 했다고 가정 => 이때도 이미 메모리에 생성되어 있는 1이라는 값을 참조

즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라봄
number === secondNumber 값이 true 가 된다.

B. 원시 데이터가 아닌 경우 (객체, 배열, 함수)

let 객체1 = {name: ‘kim’} 이라는 값을 선언하면 메모리에 객체1이 저장
let 객체2 = {name: ‘kim’} 이라고 같은 값을 선언하면 객체2 메모리 공간에 새롭게 저장

결국 객체1 === 객체2 는 false 가 된다.

(3) 데이터를 수정하면 어떻게 되나?

A. 원시 데이터의 경우

기존 1이던 number를 number = 2 로 새로 값을 할당하면, 메모리에서는?
원시 데이터는 불변성이 있기에 기존 메모리에 저장 되어있는 1 의 값은 변하지 않음
=> 새로운 메모리 저장공간에 2가 생기고 number는 새로운 메모리 공간에 저장된 2를 참조

secondNumber 를 콘솔에 찍으면 여전히 ' 1 ' 이라고 뜬다.
=> number와secondNumber는 각각 다른 메모리 저장공간을 참조 하기 때문*

B. 원시 데이터가 아닌 경우 (객체, 배열, 함수)

객체1.name = ‘park’ 로 새로운 값을 할당한다면?
객체는 불변성이 없기에 기존 메모리 저장공간에 있는 {name: ‘kim’} 이라는 값이 {name : ‘park’} 으로 바뀔뿐

정리
원시데이터 : 수정 시 메모리에 저장된 값 자체가 바뀌지 않음, 새로운 메모리 저장공간에 새로운 값 저장
그 외 데이터 : 기존 저장되어있던 메모리 저장공간의 값 자체를 바꿈

2. 리액트에서 불변성이 가지는 의의 💥

💡 리액트에서는 화면을 리렌더링 할지 말지 결정할 때 state의 변화를 확인한다.
=> state 변화 유무를 확인하는 방법 : state의 변화 전, 후의 메모리 주소를 비교💥

  • state가 변했다? 리렌더링 해 ~
  • state가 안 변했다? 리렌더링 하지마 ~

리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 하면 값은 바뀌지만 메모리주소는 변함이 없음
=> 💡 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못함 => 리렌더링이 제대로 수행 되지 않음

3. 리액트 불변성 지키기 예시

배열을 setState 할 때 불변성을 지켜주기 위해 직접 수정을 가하지 않고
전개 연산자(...)를 사용해서 기존의 값을 복사 후 값을 수정(추가)하는 식으로 구현
(배열.push 등은 불변성을 깨뜨리니까 사용x)

  import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState(["말티즈"]);

  function onClickHandler() {
		// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다. 
	  // 그리고 나서 항목을 추가합니다.
    setDogs([...dogs, "시고르자브르종"]);
  }

  console.log(dogs);
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

4. 불변성 유지하는 코드 작성 [실습]

😩 아래 코드는 불변성을 위반 하는 코드이다. 불변성을 유지하는 코드로 변경해보시오

  import React, { useState } from 'react';

function BadCounter() {
  const [items, setItems] = useState([1, 2, 3]);

  const addItem = () => {
    items.push(items.length + 1); // 배열에 직접 push, 불변성 위반
    setItems(items); // 상태 업데이트
  };

  return (
    <div>
      <button onClick={addItem}>Add Item</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

😎 변경 후
=> 새 배열 생성 -> 기존 항목 복사 -> 새 항목 추가

  import React, { useState } from 'react';

function GoodCounter() {
  const [items, setItems] = useState([1, 2, 3]);

  const addItem = () => {
    // 새 배열을 생성하고 기존 항목을 복사한 후 새 항목 추가
    setItems([...items, items.length + 1]); // 불변성 유지
  };

  return (
    <div>
      <button onClick={addItem}>Add Item</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

0개의 댓글