[React 입문] React State

Habin Lee·2023년 11월 3일
2

요약

  1. State가 무엇인지 설명할 수 있다.
  2. useState Hook을 사용할 수 있다.
  3. setState를 통해 state를 제어할 수 있다.
  4. ※ state와 onClick, onChange 메서드를 결합하여 이벤트를 만들 수 있다.
  5. 불변성이 리액트에서 무엇을 의미하는지, 왜 중요한지 설명할 수 있다.

State

State란

컴포넌트 내부에서 바뀔 수 있는 값

  • JS의 변수나 상수를 사용하지 않고 state를 쓰는 목적은 UI(엘리먼트)를 바꾸기 위함!

State 만들기

State를 만들 때는 useState()를 사용한다. <- 리액트에서의 함수 형태

useState()

useState('initial Value'); // 초기값을 넣는다.
// -> 배열형태로 두 가지 값을 return한다.
// -> [state(초기값으로 생성된 state), setstate(state를 다룰 수 있는 메서드로, 앞에 set을 붙여준다.)]

// 구조분해할당으로 state를 만들면 아래와 같은 형태가 된다.
const [state, setstate] = useState('initial Value');

예시

import React from 'react';

function App() {
  const [count, setCount] = useState(0); // 초기값이 0인 카운트
  const [todoList, setTodoList] = useState([]); // 초기값이 빈 배열인 todolist
  
  return <div></div>
}

export default App;
  • Hook: 기능 -> useState Hook: 기능을 하는 어떠한 함수를 칭한다.
  • useState Hook의 종류는 많다. -> useState, useEffect, useContext, useMemo 등

화면이 다시 렌더링되는 두 가지 예시

예시 1. Click을 눌렀을때 content 변경

  • 리액트에서 변경되는 값은 반드시 state로 관리해야함 -> UI를 바꾸기 위함
import React, { useState } from 'react';

function App() {
  // useState 초기값으로 '김할아버지' 설정
  const  [name, setName] = useState('김할아버지')

  return (
    <div>
      {name}
      // 줄바꿈 태그
      <br />
      // 버튼에 onClick을 먹이고 함수 setName으로 '박할아버지' 설정
      <button onClick={function(){
        setName('박할아버지')
      }}>클릭</button>
    </div>
  );
}

export default App;

//  -> 클릭을 했을 때, '김할아버지'가 '박할아버지'로 변경됨

예시 2. 인풋 태그 안에서 값을 입력했을 때 content 변경

  • onChange에서는 event가 매개변수로서 딸려온다.
import { useState } from 'react';

function App() {
  // 빈 문자열이 초기값인 useState 설정
  const [fruit, setFrult] = useState("");

  return (
    <div>
      과일 : {" "}
      <input
        // input에 들어오는 값으로 {}안에 fruit를 넣고
        value={fruit}
        // onChange를 먹이면 event가 매개변수로서 딸려온다.
        onChange={function (event) {
          // 우리에게 유의미한 값은 "event.target.value" -> 외우자!
          // 모르겠으면 console.log를 찍어보고 유의미한 값을 찾아야함
          setFrult(event.target.value);
        }}
      />
      <br /> <br />
      // input 태그 아래에 값이 바로 보일 수 있도록 {fruit} 넣어주기
      {fruit}
    </div>
  );
}

export default App;

// 입력하는 대로 아래에 값이 똑같이 적혀 나온다.

useState 연습하기

만들고 싶은 UI

  1. 아이디와 비밀번호에 값을 입력하고 [로그인] 버튼을 누르면 alert로 고객이 입력한 값을 알려주기
  2. alert의 메시지 내용은 다음과 같이 출력
    "고객님이 입력하신 아이디는 '입력아이디'이며, 비밀번호는 '입력비밀번호'입니다."
  3. 아이디 필드와 비밀번호 필드의 값은 state로 관리하고, 변경이 일어날 때마다 setState를 사용하여 동기화
  4. alert를 띄운 후 아이디와 비밀번호 영역을 초기화 시켜주기(빈 값으로)
  5. 비밀번호는 보이면 안됨

구현된 UI

완성코드

import React, { useState } from "react";

function App() {
  // 아이디와 비밀번호를 state로 관리하기 위해 아래와 같이 설정
  const [id, setId] = useState("");
  const [pw, setPw] = useState("");

  // 아이디 필드가 변경될 경우의 이벤트 -> 함수가 길어지기 때문에 변수로 지정하여 사용
  const onIdChangeHandler = (event) => {
    // 가져오는 값은 우리에게 유의미한 값인 event.target.value 로 설정
    setId(event.target.value);
  };
  // 비밀번호 필드가 변경될 경우의 이벤트
  const onPwChangeHandler = (event) => {
    setPw(event.target.value);
  };

  return (
    <div>
      <div>
      // input value는 {id} 값으로 지정하고 onChange에 이벤트를 넣어준다.
      아이디 : <input type='text' value={id} onChange={onIdChangeHandler} />
      </div>
      <div>
      비밀번호 :{" "}
      // 비밀번호는 보이면 안되기 때문에 type을 psaaword로 지정한다.
      <input type='password' value={pw} onChange={onPwChangeHandler} />
      </div>
      <button
        // 버튼에 onClick을 먹여준다.
        onClick={function () {
          alert(
            // 안쪽에 입력 값이 들어가야하므로 백틱으로 적어주는 것이 편하다.
            `고객님이 입력하신 아이디는 ${id}이며, 비밀번호는 ${pw}입니다.`
          );
          // alert 이후 setstate를 통해 빈 값으로 초기화 시켜준다.
          setId('');
          setPw('');
        }}>
        로그인
      </button>
    </div>
  );
}

export default App;

불변성

메모리에 있는 값을 변경할 수 없는 것.

JS에서의 데이터 할당 과정 되새기기

예시 1

// 원시 데이터: 숫자, 문자, 불리언 등 -> 값을 바로 저장하여 변수가 바라보게 됨
let number = 1;
let secondNumber = 1;

console.log(number === secondNumber) // true가 나옴

// 원시 데이터가 아닌 것들: 객체, 배열, 함수 등
// -> 입력된 값과 변수의 주소 값이 따로 저장되고 변수는 주소 값을 바라보게 됨
let obj1 = {
  name: 'kim', 
}
// obj1과 같은 값이지만 입력된 값과 변수의 주소 값을 또 따로 저장하기 때문에 obj1과 obj2는 결과적으로는 다른 주소를 가지고 있기 때문에 서로 다르다는 결과 값이 나온다.
let obj2 = {
  name: 'kim', 
}

console.log(obj1 === obj2) // false가 나옴

예시 2 - 데이터를 수정하는 경우

  • 원시데이터에 저장된 값 자체를 바꿀 수 없음 -> 새로운 메모리 공간을 생성함 : 불변성 O
  • 원시데이터가 아닌 것 -> 기존에 저장되어 있던 공간을 바꿈 : 불변성 X
// 원시 데이터: 숫자, 문자, 불리언 등 -> 값을 바로 저장하여 변수가 바라보게 됨
let number = 1;
let secondNumber = 1;

number = 2;
// 1이라는 값은 그대로 두고 2라는 값만 저장하여 number라는 변수가 2를 바라보게 됨
// secondNumber는 그대로 있는 1이라는 값을 그대로 바라보고 있음
// 메모리 주소가 변함 -> 원시데이터는 불변성이 있다.

// 원시 데이터가 아닌 것들: 객체, 배열, 함수 등
// -> 입력된 값과 변수의 주소 값이 따로 저장되고 변수는 주소 값을 바라보게 됨
let obj1 = {
  name: 'kim', 
}

obj1.name = 'park' 
// 결과적으로 객체는 불변성이 없다.
// obj1의 name값은 바뀌었으나, obj1은 처음 주소 값을 그대로 바라보고 있음

let obj2 = {
  name: 'kim', 
}

React의 Re-Rendering

  • 컴포넌트가 그려지기 위해서 Rendering이 이루어진다.
  • Rendering = 변경된 값이 보여지는 화면에도 바로 바로 변경되는 것
  • Rendering이 되는 조건 = state가 바뀌면 됨
  • Re-Rendering = 변경된 값으로 화면에 다시 그려주는 것

    ※ 리액트는 화면을 렌더링할지를 state의 변화에 따라 결정한다.(단순 변수는 무시)

예시 - 오류

import React, { useState } from 'react'

function App() {
  let count = 0;
  const [input, setInput] = useState("");
  return (
    <div>
      <input
        value={input}
        onChange={(event) => {
          // setInput으로 re-rendering
          setInput(event.target.value);
        }}
      />
      {input}
      <button onClick={() => {
        // 단순 변수는 무시
        count++;
        // console.log로는 값이 올라가는 것을 알 수 있다.
        console.log(`count는 ${count}입니다.`);
      }}>증가</button>
      {count}
    </div>
  )
}

export default App

예시 - 수정

import React, { useState } from 'react'

function App() {
  // count도 useState로 관리
  const [count, setCount] = useState(0);
  const [input, setInput] = useState("");
  return (
    <div>
      <input
        value={input}
        onChange={(event) => {
          setInput(event.target.value);
        }}
      />
      {input}
      <br />
      <button onClick={() => {
        // 클릭했을 때 숫자가 하나씩 증가하도록 newCount 변수 지정
        const newCount = count + 1;
        // setCount로 newCount 변수 입력
        setCount(newCount);
      }}>증가</button>
      {count}
    </div>
  )
}

export default App

React에서 불변성을 지키는 방법

예시 - 오류

  • 객체는 원시데이터가 아니기 때문에 불변성이 없다.
  • 이 문제를 해결하기 위해서 새로운 객체를 만들어주어야 한다.
import React, { useState } from "react";

function App() {
  // 객체 state
  const [obj, setObj] = useState({
    name: "1habin",
    age: 21,
  });
  return (
  <div>
    <div>{obj.name}</div>
    <button onClick={() => {
      // 클릭했을 때 아래의 이름으로 바꾸고 싶다.
      obj.name = '2habin';
      // console.log로는 name 값이 바뀌지만
      console.log(obj)
      // 보여지는 화면에서는 re-rendering이 되지 않음
      setObj(obj)
    }}>변경!</button>
  </div>)
}

export default App;

예시 - 수정

  • 원시데이터가 아닌 것들은 spread, map, filter 등 불변성을 해치지 않는 방법으로 처리를 해주어야한다.
import React, { useState } from "react";

function App() {
  const [obj, setObj] = useState({
    name: "1habin",
    age: 21,
  });
  return (
  <div>
    <div>{obj.name}</div>
    <button onClick={() => {
      // 바뀔 이름을 먼저 선언을 해주고
      obj.name = '2habin'
      // ...(spread)를 사용하여 객체를 펼쳤다가 다시 모아 새로운 객체를 만든다.
      const obj2 = {...obj}
      // 변경을 누르면 이름이 바뀌게 된다.
      setObj(obj2)
    }}>변경!</button>
  </div>)
}

export default App;

느낀 점

처음에 강의를 따라서 코드를 적다가 console.log에 찍히는 값과 rendering되어 화면에 보이는 값이 달라서 당황했었다. 강의를 들을 때는 내용이 이해가 되다가도 백지에서 다시 코드를 작성하려고 보니 내 머릿속도 하얗게.. 되었다.. 곧 리액트 심화과정으로 넘어가는데, 리액트 강의는 자바스크립트 강의보다 더 많이 봐야할 것 같다😂

0개의 댓글