React

[React #21] State

PEAZH 2024. 3. 6. 16:42
반응형

01. State

  • 현재 가지고 있는 모양이나 형태를 정의하는 값으로 변화할 수 있는 동적인 값이기도 함
  • 리액트의 컴포넌트들은 모두 다 자신의 형태나 모양을 정의하는 State를 가질 수 있음
  • 현재 상태를 보관하는 변수, 상태에 따라서 각각 다른 UI를 화면에 렌더링할 수 있음
  • 하나의 컴포넌트에 여러 개의 State를 만드는 것도 가능함

💡리렌더링?

더보기

컴포넌트가 다시 렌더링 되는 상황, '리렌더'라고도 함

 

 

02. State 생성 방법

01) import

import { useState } from 'react';

 

02) useState 콘솔 출력하기

import './App.css';
import { useState } from 'react';

export default function App() {
  const state = useState();
  console.log(state);

  return <></>;
}

콘솔창

 

 

📌 useState()

- 새로운 state를 생성하는 함수로 두 개의 요소를 담은 배열을 반환

- 첫 번째 요소 : state의 현재 값, 두 번째 요소 : state를 변경시키는 상태 변화 함수

- 상태 변화 함수는 비동기로 동작

- 배열을 반환함으로 보통 비 구조화 할당 문법을 이용함

import './App.css';
import { useState } from 'react';

export default function App() {
  const [state, setState] = useState();

  return (
    <>
      <h1>{state}</h1>
    </>
  );
}
 

 

 

03. State 활용

01) 버튼을 클릭하면 값 증가하기

import './App.css';
import { useState } from 'react';

export default function App() {
  const [state, setState] = useState(0);

  return (
    <>
      <h1>{state}</h1>
      <button
        onClick={() => {
          setState(state + 1);
        }}
      >
        +
      </button>
    </>
  );
}

실행화면

 

- 컴포넌트 내에 새로운 state를 생성하고 state의 값을 변경하면, 리액트가 내부적으로 컴포넌트의 state가 변경되었다는 것을 감지해서 이 컴포넌트를 리렌더링

- 컴포넌트 역할을 하는 함수를 다시 호출하고 새롭게 반환함

02) 버튼으로 토글 효과 주기

import './App.css';
import { useState } from 'react';

export default function App() {
  const [light, setLight] = useState('OFF');

  return (
    <>
      <div>
        <h1>{light}</h1>
        <button
          onClick={() => {
            setLight(light === 'OFF' ? 'ON' : 'OFF');
          }}
        >
          {light === 'OFF' ? '켜기' : '끄기'}
        </button>
      </div>
    </>
  );
}

실행화면

 

💡자바스크립트 변수로 변경하지 않고 State를 쓰는 이유?

더보기

변수의 값이 바뀐다고 컴포넌트가 리렌더링 되지 않음

리액트는 state의 값이 변환했을 때에만 리렌더링이 일어남

→ 그래서 리액트는 가변적인 값을 관리할 때 State를 이용해서 처리함

 

03) props 이용해서 state를 자식 컴포넌트에게 전달

import './App.css';
import { useState } from 'react';

// 전구 역할을 하는 컴포넌트
const Bulb = ({ light }) => {
  return (
    <div>
      {light === 'ON' ? (
        <h1 style={{ backgroundColor: 'orange' }}>ON</h1>
      ) : (
        <h1 style={{ backgroundColor: 'gray' }}>OFF</h1>
      )}
    </div>
  );
};

export default function App() {
  const [count, setCount] = useState(0);
  const [light, setLight] = useState('OFF');

  return (
    <>
      <div>
        <Bulb light={light} />
        <button
          onClick={() => {
            setLight(light === 'OFF' ? 'ON' : 'OFF');
          }}
        >
          {light === 'OFF' ? '켜기' : '끄기'}
        </button>
      </div>
      <hr></hr>
      <div>
        <h1>{count}</h1>
        <button
          onClick={() => {
            setCount(count + 1);
          }}
        >
          +
        </button>
      </div>
    </>
  );
}

실행화면

 

- 리액트 컴포넌트들은 자신이 갖는 state가 변경되지 않아도부모로부터 받는 props의 값이 변경되면 리렌더링 됨

- 그래서 count의 값을 변경해도 Bulb도 리렌더링 됨

- 불필요하게 리렌더링 경우가 많아지면 성능이 떨어지기 때문에 컴포넌트를 분리하는게 좋음

 

💡 컴포넌트가 리렌더링 할 때

더보기

1. 자신이 관리하는 state의 값이 변경되었을 때

2. 자신이 제공받는 props의 값이 변경되었을 때

3. 부모 컴포넌트가 리렌더링 될 때

 

04) 컴포넌트 분리

import './App.css';
import { useState } from 'react';

// 전구 역할을 하는 컴포넌트
const Bulb = () => {
  const [light, setLight] = useState('OFF');
  return (
    <div>
      {light === 'ON' ? (
        <h1 style={{ backgroundColor: 'orange' }}>ON</h1>
      ) : (
        <h1 style={{ backgroundColor: 'gray' }}>OFF</h1>
      )}
      <button
        onClick={() => {
          setLight(light === 'OFF' ? 'ON' : 'OFF');
        }}
      >
        {light === 'OFF' ? '켜기' : '끄기'}
      </button>
    </div>
  );
};

// 카운터 컴포넌트
const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <h1>{count}</h1>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        +
      </button>
    </div>
  );
};

export default function App() {
  return (
    <>
      <Bulb />
      <hr></hr>
      <Counter />
    </>
  );
}

- 전구 컴포넌트와 카운터 컴포넌트를 분리해서 불필요한 리렌더링을 막음

 

 

04. 사용자의 입력값 받기

01) 이름

import React from 'react';
import { useState } from 'react';

function Register() {
  const [name, setName] = useState('');

  const onChangeName = (e) => {
    setName(e.target.value);
  };

  return (
    <>
      <div>
        <input value={name} onChange={onChangeName} placeholder='이름' />
        {name}
      </div>
    </>
  );
}

export default Register;

- 이벤트 객체의 target에 value는 현재 인풋에 저장되어 있는 값을 보관하고 있음

- e.target.value : 사용자가 인풋에 작성한 텍스트에 접근

- value 값 꼭 설정하기⭐


✏️ setName(e.target.value);

① input에 값을 입력했을 때 onChangeName 함수 실행

② setName 함수를 호출하면서 현재 이 input에 사용자가 입력한 텍스트를 'name'에 전달

 

02) 생일

import React from 'react';
import { useState } from 'react';

function Register() {
  const [birth, setBirth] = useState('');
 
  const onChangeBirth = (e) => {
    setBirth(e.target.value);
  };

  return (
    <>   
      <div>
        <input value={birth} onChange={onChangeBirth} type='date' />
        {birth}
      </div>
    </>
  );
}

export default Register;

03) 국적

import React from 'react';
import { useState } from 'react';

function Register() {
  const [country, setCountry] = useState('');

  const onChangeCountry = (e) => {
    setCountry(e.target.value);
  };

  return (
    <>
      <div>
        <select value={country} onChange={onChangeCountry}>
          <option></option>
          <option value={'ko'}>한국</option>
          <option value={'uk'}>영국</option>
          <option value={'us'}>미국</option>
        </select>
        {country}
      </div>
    </>
  );
}

export default Register;

- select 태그는 기본적으로  option 중 맨 위에 있는 값을 초기값으로 설정

- 비어있는 값을 하고싶다면 빈 option 태그 추가하기

- 보통은 선택지에는 친절하고 길게 텍스트를 설정하고 value에는 간결한 값을 사용함

04) 자기소개

import React from 'react';
import { useState } from 'react';

function Register() {
  const [bio, setBio] = useState('');

  const onChangeBio = (e) => {
    setBio(e.target.value);
  };

  return (
    <>
      <div>
        <textarea
          value={bio}
          onChange={onChangeBio}
          placeholder='자기소개를 입력해 주세요.'
        />
        {bio}
      </div>
    </>
  );
}

export default Register;

- textarea : input 태그와 동일하지만, 여러 줄의 입력을 받을 수 있음

 

💡 초기값을 설정해주고 싶을 때

useState("이름"); 입력해주고 input의 'value'를 state의 값을 입력해줌

 

실행화면

 

 

05. 비효율적인 코드 개선하기

01) 여러 개의 state를 객체로 만들기

const [input, setInput] = useState({
    name: '',
    birth: '',
    country: '',
    bio: '',
});

 

- useState에 초기 값으로 객체를 넣고 name, birth, country, bio 각각 프로퍼티로 설정

- 비슷한 여러 개의 state를 하나의 객체 값으로 묶어서 하나의 state로 통합해서 관리하면 편함

02) 이벤트 핸들러 합치기

  const onChange = (e) => {
    setInput({
      ...input,
      [e.target.name]: e.target.value,
    });
  };
  
   <input
      name='name'
      value={input.name}
      onChange={onChange}
      placeholder='이름'
    />

- 스프레드 연산자를 이용해서 기존의 input state에 들어있던 프로퍼티 값들은 그대로 유지하고, 변경하고 싶은 프로퍼티의 값만 바꿔주기

- 여러 개의 비슷하게 생긴 이벤트 핸들러들은 통합 이벤트 핸들러로 묶어줄 수 있음


✏️모든 input에 onChange 이벤트 함수 실행

① setInput 상태 변화 함수 호출

② 인수로는 객체를 만들어서 전달

③ ...input : 스프레드 연산자로 인풋의 값을 나열해줌

④ [e.target.name] : '프로퍼티의 key'로 이벤트가 발생하는 name으로 변수 설정

 

03) 최종 코드

import React from 'react';
import { useState } from 'react';

function Register() {
  const [input, setInput] = useState({
    name: '',
    birth: '',
    country: '',
    bio: '',
  });

  const onChange = (e) => {
    setInput({
      ...input,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <>
      <div>
        <input
          name='name'
          value={input.name}
          onChange={onChange}
          placeholder='이름'
        />
        {input.name}
      </div>
      <div>
        <input
          name='birth'
          value={input.birth}
          onChange={onChange}
          type='date'
        />
        {input.birth}
      </div>
      <div>
        <select name='country' value={input.country} onChange={onChange}>
          <option></option>
          <option value={'ko'}>한국</option>
          <option value={'uk'}>영국</option>
          <option value={'us'}>미국</option>
        </select>
        {input.country}
      </div>
      <div>
        <textarea
          name='bio'
          value={input.bio}
          onChange={onChange}
          placeholder='자기소개를 입력해 주세요.'
        />
        {input.bio}
      </div>
    </>
  );
}

export default Register;

 

 

참고자료

이정환 Winterlood, '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지'

 

반응형