Front/React

상태(State) - React 배우기

oodada 2023. 10. 31. 06:28

React 배우기 - 상태(State)

1. State

상태란 컴포넌트 내부에서 선언하며 내부에서 값을 변경할 수 있는 데이터를 의미한다.

- State와 Props의 차이

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

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

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

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

- State의 특징

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

- State를 사용하는 이유

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

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')

    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. 아이콘 클릭시 아이콘 이미지 변경

import { useState } from 'react'
import { Box } from '@chakra-ui/react'
import switchOn from './assets/images/switch-on.svg'
import switchOff from './assets/images/switch-off.svg'

export default function App() {
    const [isToggled, setIsToggled] = useState(false)

    return (
        <>
            <div>
                <img
                    src={isToggled ? switchOn : switchOff}
                    alt="switch"
                    onClick={() => {
                        setIsToggled(!isToggled)
                    }}
                />

                <div>{isToggled ? '켜짐' : '꺼짐'}</div>
            </div>
        </>
    )
}

5. menu 클릭시 active 클래스 변경

import React, { useState } from 'react'
import { AiFillCar } from 'react-icons/ai'
import { LiaUmbrellaBeachSolid } from 'react-icons/lia'
import { PiHouseLineDuotone } from 'react-icons/pi'
import { SiApple } from 'react-icons/si'
import styled from 'styled-components'

const Sort = () => {
    const sortArr = [
        { value: '최고의 전망', icon: <SiApple /> },
        { value: '해변 근처', icon: <LiaUmbrellaBeachSolid /> },
        { value: '한옥', icon: <PiHouseLineDuotone /> },
        { value: '농장', icon: <AiFillCar /> },
    ]

    const [selected, setSelected] = useState(0)

    return (
        <div>
            <SortStyled>
                {sortArr.map((item, index) => (
                    <li
                        key={index}
                        onClick={() => {
                            setSelected(index)
                        }}
                        className={selected === index ? 'active' : ''}
                    >
                        {/* // 클릭 시, selected의 상태를 변경
                    // selected의 상태가 변경되면, 화면이 다시 렌더링되어 selected === index가 true가 되어 active 클래스가 추가된다. */}
                        {item.icon}
                        {item.value}
                    </li>
                ))}
            </SortStyled>
            <div className="tab-contents">
                {sortArr.map((item, index) => (
                    <div key={index} style={{ display: selected === index ? 'block' : 'none' }}>
                        {item.value}
                    </div>
                ))}
            </div>
        </div>
    )
}

const SortStyled = styled.div`
    list-style: none;
    display: flex;
    li {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        width: 100px;
        height: 75px;
        color: #777;
        text-decoration: none;
        cursor: pointer;
        &:hover {
            color: #000;
        }
    }
    svg {
        font-size: 30px;
    }

    .active {
        border-bottom: 2px solid #000;
    }
`

export default Sort

6. FontAwesome을 사용한 아이콘 변경

// App.js
import React, { useState } from 'react' // useState를 불러온다.
import styled from 'styled-components'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBars, faTimes } from '@fortawesome/free-solid-svg-icons'

function Menu() {
    // isToggled는 상태의 이름, setIsToggled는 상태를 변경하는 함수로 구성한다.
    const [isToggled, setIsToggled] = useState(false) // useState(false)는 상태의 초기값이다.

    function toggleMenu() {
        setIsToggled(!isToggled) // isToggled의 값을 변경한다.
    }

    return (
        // isToggled의 값을 변경한다.
        // isToggled의 값을 MenuStyle 컴포넌트에 전달한다.
        <MenuStyle isToggled={isToggled}>
            <button onClick={toggleMenu}>
                // isToggled의 값에 따라 아이콘을 변경하는데 조건을 !isToggled로 설정하는 이유는 // isToggled의 값이
                false이면 faBars 아이콘을 출력하고, true이면 faTimes 아이콘을 출력하기 위해서이다.
                <FontAwesomeIcon icon={!isToggled ? faBars : faTimes} />
            </button>
            <ul className="header__menulist">
                <li>Mac</li>
                <li>iPad</li>
                <li>iPhone</li>
                <li>Watch</li>
                <li>Music</li>
                <li>고객지원</li>
            </ul>
        </MenuStyle>
    )
}

const MenuStyle = styled.div`
    .header__menulist {
        list-style: none;
        display: flex;
    }
    @media screen and (max-width: 768px) {
        .header__menulist {
            display: ${(props) => (props.isToggled ? 'flex' : 'none')};
            flex-direction: column;
            width: 100px;
            padding: 20px;
            background-color: #eee;
        }
    }
`

function App() {
    return <Menu />
}

export default App

7. 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

8. Tabs 구현

import React, { useState } from 'react'

function TabComponent() {
    // activeTab : 상태의 이름, setActiveTab : 상태를 변경하는 함수
    const [activeTab, setActiveTab] = useState('tab1')

    return (
        <div>
            <ul className="tabs">
                <li
                    role="presentation" // 클릭 가능한 요소로 설정
                    className={activeTab === 'tab1' ? 'active' : ''} // activeTab의 값이 'tab1'이면 'active' 클래스를 추가한다.
                    onClick={() => setActiveTab('tab1')} // 클릭 시, activeTab의 값을 'tab1'로 변경한다.
                >
                    tab1
                </li>
                <li
                    role="presentation"
                    className={activeTab === 'tab2' ? 'active' : ''}
                    onClick={() => setActiveTab('tab2')}
                >
                    tab2
                </li>
                <li
                    role="presentation"
                    className={activeTab === 'tab3' ? 'active' : ''}
                    onClick={() => setActiveTab('tab3')}
                >
                    tab3
                </li>
            </ul>
            <div className="tab-content">
                // activeTab의 값에 따라 화면에 출력되는 내용이 달라진다.
                {activeTab === 'tab1' && <div>this is tab1</div>}
                {activeTab === 'tab2' && <div>this is tab2</div>}
                {activeTab === 'tab3' && <div>this is tab3</div>}
            </div>
        </div>
    )
}

export default TabComponent
  • 탭의 내용을 배열로 정의한다.
import React, { useState } from 'react'

function TabComponent() {
    const [activeTab, setActiveTab] = useState('tab1')

    const tabs = [
        { id: 'tab1', title: 'tab1', content: 'this is tab1' },
        { id: 'tab2', title: 'tab2', content: 'this is tab2' },
        { id: 'tab3', title: 'tab3', content: 'this is tab3' },
    ]

    return (
        <div>
            <ul className="tabs">
                {tabs.map((tab) => (
                    <li
                        key={tab.id}
                        role="presentation"
                        // activeTab의 값이 tab.id와 같으면 'active' 클래스를 추가한다.
                        className={activeTab === tab.id ? 'active' : ''}
                        // 클릭 시, activeTab의 값을 tab.id로 변경한다.
                        onClick={() => setActiveTab(tab.id)}
                    >
                        {tab.title}
                    </li>
                ))}
            </ul>
            <div className="tab-content">
                {tabs.map((tab) => (
                    // activeTab의 값에 따라 화면에 출력되는 내용이 달라진다.
                    <div key={tab.id} style={{ display: activeTab === tab.id ? 'block' : 'none' }}>
                        {tab.content}
                    </div>
                ))}
            </div>
        </div>
    )
}

export default TabComponent
티스토리 친구하기