Front/React

상태(State) - React 배우기

oodada 2023. 10. 31. 06:28

React 배우기 - 상태(State)

1. State

상태(State)란 컴포넌트가 가질 수 있는 동적인 데이터를 의미합니다.

  • 사용자가 입력한 텍스트
  • 현재 선택된 메뉴 항목
  • 토글 버튼의 on/off 상태
  • 서버에서 받아온 데이터

useState 기본 문법 설명

const [state, setState] = useState(initialValue);
// - state: 현재 상태 값
// - setState: 상태를 변경하는 함수
// - initialValue: 초기 상태 값

- State와 Props의 차이

  • 리액트 컴포넌트는 입력과 출력이 있는데 prop를 통해 입력된 데이터를 우리가 만든 컴포넌트 함수가 처리해서 retrun을 통해 출력한다.

  • props는 컴포넌트를 사용하는 외부자를 위한 데이터이다.

  • state는 props와 비슷하지만 컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있다.

  • state는 컴포넌트 자신을 위한 데이터이다.

- State의 특징

  • 상태는 객체 형태의 데이터이다.
  • 상태는 컴포넌트 내부에서 선언되어야 한다.
  • 상태는 props와 달리 컴포넌트 내부에서 값을 변경할 수 있다.

- State를 사용하는 이유

  • 사용자와의 상호작용을 통해 동적인 웹 페이지를 만들기 위해서 사용한다.
  • 상태가 변경되면 컴포넌트는 다시 렌더링된다.

- State 동작 원리

  • 기본 구조
// useState의 반환값
const [state변수, state변경함수] = useState(초기값);
  • 동작 원리
// useState는 항상 2개의 요소를 가진 배열을 반환합니다
const array = useState('winter');
// array = ['winter', function()]

// 이것을 풀어서 쓰면
const name = array[0]; // 'winter'
const setName = array[1]; // setState 함수

// 구조 분해 할당으로 한 번에 쓰면
const [name, setName] = useState('winter');

2. State 사용

- 상태를 사용하지 않고 버튼 클릭시 이름 변경

  • 상태를 사용하지 않으면 버튼을 클릭해도 이름이 변경되지 않는다.
  • console.log()를 통해 name의 값이 변경되는 것을 확인할 수 있다.
// App.js
import React from 'react';

function App() {
  let name = 'winter'; // name 변수에 'winter'라는 값을 저장한다.

  // changeName() 함수를 정의한다.
  function changeName() {
    // name의 값이 'winter'이면 'fall'로, 'fall'이면 'winter'로 변경한다.
    name = name === 'winter' ? 'fall' : 'winter';
    console.log(name); // name의 값을 출력한다.
  }
  return (
    // name의 값을 출력한다.
    // 버튼을 클릭하면 changeName() 함수를 실행한다.
    <div className='root'>
      <h1>{name}</h1>
      <button onClick={changeName}>Name Change</button>
    </div>
  );
}

export default App;

- Vanilla 자바스크립트를 활용한 버튼 클릭시 이름 변경

  • 상태를 사용하지 않고 Vanilla js를 활용해 버튼을 클릭시 이름을 변경할 수 있다.
  • 하지만 이 방법은 리액트에서 권장하지 않는 방법이다.
// App.js
import React from 'react';

function App() {
  let name = 'winter'; // name 변수에 'winter'라는 값을 저장한다.

  // changeName() 함수를 정의한다.
  function changeName() {
    // name의 값이 'winter'이면 'fall'로, 'fall'이면 'winter'로 변경한다.
    name = name === 'winter' ? 'fall' : 'winter';
    console.log(name); // name의 값을 출력한다.
    document.querySelector('h1').innerText = name; // h1 태그의 텍스트를 name의 값으로 변경한다.
  }
  return (
    <div className='root'>
      <h1>{name}</h1>
      <button onClick={changeName}>Name Change</button>
    </div>
  );
}

export default App;

- State(상태)를 사용해 버튼 클릭시 이름 변경

  • State(상태)를 사용해 버튼을 클릭시 이름을 변경한다.
  • State(상태)를 사용하면 컴포넌트가 다시 렌더링된다.
// App.js
// useState를 불러온다.
import React, { useState } from 'react';

function App() {
  // name : 상태의 이름, setName : 상태를 변경하는 함수
  // useState('winter') : 상태의 초기값
  const [name, setName] = useState('winter');
  // 배열의 구조 분해 할당
  // const name = useState('winter')[0];
  // const setName = useState('winter')[1];
  // useState('winter') : 상태의 초기값

  function changeName() {
    // 상태를 변경하는 함수(setName)를 사용해 name의 값을 변경한다.
    // name의 값이 'winter'이면 'fall'로, 'fall'이면 'winter'로 변경한다.
    // 삼항 연산자 : 조건 ? 참일 때 값 : 거짓일 때 값
    setName(name === 'winter' ? 'fall' : 'winter');
  }

  return (
    // button을 클릭하면 changeName() 함수를 등록합니다.
    <div className='root'>
      <h1>{name}</h1>
      <button onClick={changeName}>Name Change</button>
    </div>
  );
}

export default App;

3. 버튼 클릭시 숫자 변경

  • State(상태)를 사용해 버튼을 클릭시 숫자를 변경한다.
  • State(상태)를 사용하면 컴포넌트가 다시 렌더링된다.
// App.js
import React, { useState } from 'react'; // useState를 불러온다.

function App() {
  const [number, setNumber] = useState(0);

  function increase() {
    setNumber(number + 1);
  }

  function decrease() {
    setNumber(number - 1);
  }
  return (
    <div className='root'>
      <h1>{number}</h1>
      <button onClick={increase}>+</button>
      <button onClick={decrease}>-</button>
    </div>
  );
}

export default App;

4. 메뉴 토글 버튼

<button
  onClick={() => {
    setIsOpen(!isOpen);
  }}
>
  {/* {isOpen ? 'x' : '='} */}
  {isOpen ? <IoCloseSharp /> : <FaBars />}
</button>

5. 에어비앤비 선택 메뉴 활성화

'use client';

import React, { useState } from 'react';
import { TbTicket } from 'react-icons/tb'; // 컬쳐 아이콘
import { GiTreehouse } from 'react-icons/gi'; // 한적한 시골
import { MdOutlinePhotoCamera } from 'react-icons/md'; // 최고의 전망
import { BiHome } from 'react-icons/bi'; // 한옥

const Sort = () => {
  const sortArr = [
    { value: '컬쳐 아이콘', icon: <TbTicket /> },
    { value: '한적한 시골', icon: <GiTreehouse /> },
    { value: '최고의 전망', icon: <MdOutlinePhotoCamera /> },
    { value: '한옥', icon: <BiHome /> },
  ];

  const [selected, setSelected] = useState(0);

  return (
    <div className='max-w-6xl mx-auto px-4'>
      <div
        className='flex justify-between items-center gap-8 border-b'
        role='tablist'
        aria-label='숙소 카테고리'
      >
        {sortArr.map((item, index) => (
          <button
            key={index}
            onClick={() => setSelected(index)}
            className={`
              flex flex-col items-center justify-center
              py-4 gap-2 flex-1
              transition-all duration-200 ease-in-out
              focus:outline-none
              ${selected === index ? 'text-black border-b-2 border-black' : 'text-gray-500 hover:text-black hover:border-b-2 hover:border-gray-300'}
            `}
            role='tab'
            aria-selected={selected === index}
          >
            <span
              className='text-2xl'
              aria-hidden='true'
            >
              {item.icon}
            </span>
            <span className='text-xs font-medium whitespace-nowrap'>{item.value}</span>
          </button>
        ))}
      </div>

      <div className='mt-4'>
        {sortArr.map((item, index) => (
          <div
            key={index}
            role='tabpanel'
            aria-hidden={selected !== index}
            className={`
              transition-all duration-200 ease-in-out
              ${selected === index ? 'block' : 'hidden'}
            `}
          >
            {item.value} 관련 숙소가 표시됩니다.
          </div>
        ))}
      </div>
    </div>
  );
};

export default Sort;

6. Input 상태 관리

  • State(상태)를 사용해 input의 상태를 관리한다.
// App.js
import React, { useState } from 'react'; // useState를 불러온다.

function App() {
  // 상태를 정의한다. text는 상태의 이름, setText는 상태를 변경하는 함수이다.
  // useState('')은 상태의 초기값이다.
  const [text, setText] = useState('');

  // input의 값을 변경했을 때 실행되는 함수이다.
  // e.target.value : input의 value 속성의 값을 가져온다.
  function handleChange(e) {
    setText(e.target.value); // setText() 함수를 사용해 text의 값을 변경한다.
    // text 상태 값이 변경되면 컴포넌트가 다시 렌더링되기 때문에 text 상태 값이 출력된다.
  }

  return (
    // input의 값을 출력한다.
    // input의 값이 변경되면 handleChange() 함수를 실행한다.
    <div className='root'>
      <input
        type='text'
        value={text}
        onChange={handleChange}
      />
      <p>{text}</p>
    </div>
  );
}

export default App;

7. navigation 구현

import { NavLink } from 'react-router-dom';

const Navigation = () => {
  // NavLink의 className을 위한 공통 함수
  const getLinkStyle = ({ isActive }) => {
    return `
      transition-colors duration-200
      hover:text-yellow-500
      ${isActive ? 'text-yellow-500' : 'text-white'}
    `;
  };

  return (
    <nav>
      <ul className='flex space-x-4'>
        <li>
          <NavLink
            to='/'
            className={getLinkStyle}
          >
            Home
          </NavLink>
        </li>
        <li>
          <NavLink
            to='/about'
            className={getLinkStyle}
          >
            About
          </NavLink>
        </li>
        <li>
          <NavLink
            to='/contact'
            className={getLinkStyle}
          >
            Contact
          </NavLink>
        </li>
      </ul>
    </nav>
  );
};

export default Navigation;
티스토리 친구하기