Front/React

useReducer - React 배우기

oodada 2024. 3. 19. 09:47
반응형

useReducer

useReducer 란?

useReduceruseState 보다 더 다양한 컴포넌트 상태를 관리하는 Hook 입니다. useState 는 컴포넌트 상태를 관리하는 가장 기본적인 Hook 이지만, useReducer 는 복잡한 상태 로직을 다루기에 더 적합합니다.

컴포넌트에서 상태 변화 코드를 쉽게 분리할 수 있고, 상태 로직을 컴포넌트 바깥으로 빼내어 재사용할 수 있습니다.

useReducer 사용법

useReducer 는 다음과 같이 사용합니다.

const [state, dispatch] = useReducer(reducer, initialState)
  • state : 현재 상태
  • dispatch : 액션을 발생시키는 함수, 액션을 발생시키면 리듀서 함수가 호출되어 상태가 변경됩니다.
  • reducer : 상태를 변경하는 함수
  • initialState : 초기 상태

reducer 함수는 다음과 같이 작성합니다.

reducer 함수는 현재 상태와 액션을 받아서 새로운 상태를 반환합니다.
action.type 은 대문자로 작성하여 액션 타입이 상수임을 나타냅니다.

function reducer(state, action) {
    switch (action.type) {
        case 'ACTION_TYPE1':
            return { ...state, ...action.payload }
        case 'ACTION_TYPE2':
            return { ...state, ...action.payload }
        default:
            return state
    }
}

useReducer 예제

- useStaet 사용

useState 를 사용하여 컴포넌트 상태를 관리하는 예제로 상태관리가 복잡해질수록 코드가 복잡해 관리가 어려워집니다.
또한 상태를 변경하는 로직이 컴포넌트 안에 선언되어 있어 재사용이 어렵습니다.

import React, { useState } from 'react'

function Counter() {
    const [count, setCount] = useState(0)

    const onIncrease = () => {
        setCount(count + 1)
    }

    const onDecrease = () => {
        setCount(count - 1)
    }

    return (
        <div>
            <h1>{count}</h1>
            <button onClick={onIncrease}>+1</button>
            <button onClick={onDecrease}>-1</button>
        </div>
    )
}

export default Counter

- useReducer 사용

useReducer 를 사용하여 컴포넌트 상태를 관리하는 예제로 상태관리가 복잡해져도 코드가 간결해집니다.
또한 상태를 변경하는 로직이 컴포넌트 바깥에 선언되어 있어 재사용이 용이합니다.

dispatch 함수를 이용하여 액션을 발생시키면 reducer 함수가 호출되어 상태가 변경됩니다. 상태가 변경되면 컴포넌트가 리렌더링됩니다.

쉽게 말해, dispatch가 파견 나간 직원이라 생각하고, reducer가 파견 받은 회사라 생각하면 이해하기 쉽습니다. 파견 나간 직원이 정보를 보내면 파견 받은 회사에서 정보를 처리하고, 결과를 돌려줍니다.

// src/components/Counter.js
import React, { useReducer } from 'react'

// reducer 함수로 상태를 변경
// state : 현재 상태
// action : 상태를 변경할 때 참조하는 값
function reducer(state, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { counter: state.counter + 1 }
        case 'DECREMENT':
            return { counter: state.counter - 1 }
        case 'RESET':
            return { counter: 0 }
        default:
            return state
    }
}

function Counter() {
    // useReducer 로 상태 관리
    // state : 현재 상태
    // dispatch : 액션을 발생시키는 함수
    // reducer : 상태를 변경하는 함수
    // initialState : 초기 상태
    const [state, dispatch] = useReducer(reducer, {
        counter: 0,
    })

    // state : { counter: 0 }
    return (
        <div>
            <h1>Counter</h1>
            <p>Current count: {state.counter}</p>
            <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
            <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
            <button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
        </div>
    )
}

export default Counter

useReducer 여러 상태 사용

useReducer 를 사용하여 여러 상태를 관리할 수 있습니다.

import React, { useReducer } from 'react'

function reducer(state, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { ...state, counter: state.counter + 1 }
        case 'DECREMENT':
            return { ...state, counter: state.counter - 1 }
        case 'RESET':
            return { ...state, counter: 0 }
        default:
            return state
    }
}

function Counter() {
    const [state, dispatch] = useReducer(reducer, {
        counter: 0,
        name: 'counter',
    })
    // state : { counter: 0, name: 'counter' }

    return (
        <div>
            <h1>
                {state.name}: {state.counter}
            </h1>
            <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
            <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
            <button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
        </div>
    )
}

export default Counter

할 일 관리 앱 상태 관리

앞에서 만든 할 일 관리 앱 상태 관리 예제를 useReducer 로 변경해보겠습니다.

// src/components/App.js
import React, { useReducer } from 'react'
import TodoHd from './TodoHd'
import TodoEditor from './TodoEditor'
import TodoList from './TodoList'

const mockTodo = [
    {
        id: 1,
        isDone: false,
        task: '고양이 밥주기',
        createdDate: new Date().getTime(),
    },
    {
        id: 2,
        isDone: false,
        task: '감자 캐기',
        createdDate: new Date().getTime(),
    },
    {
        id: 3,
        isDone: false,
        task: '고양이 놀아주기',
        createdDate: new Date().getTime(),
    },
]

// reducer 함수로 상태를 변경합니다.
// state : 현재 상태
// action : 상태를 변경할 때 참조하는 값
function reducer(state, action) {
    switch (action.type) {
        case 'ADD':
            return [action.payload, ...state]
        case 'UPDATE':
            return state.map((it) => (it.id === action.payload ? { ...it, isDone: !it.isDone } : it))
        case 'DELETE':
            return state.filter((it) => it.id !== action.payload)
        default:
            return state
    }
}

function App() {
    // useReducer를 이용하여 상태를 관리합니다.
    // 첫 번째 인수로 reducer 함수를, 두 번째 인수로 초기 상태를 전달합니다.
    // 반환값으로 state와 dispatch 함수를 받습니다.
    // state : 현재 상태
    // dispatch : action을 발생시키는 함수
    // dispatch 함수를 이용하여 action을 발생시키면 reducer 함수가 호출됩니다.
    // reducer 함수가 호출되면 state가 변경됩니다.
    // state가 변경되면 컴포넌트가 리렌더링됩니다.
    const [todo, dispatch] = useReducer(reducer, mockTodo)

    // todo : mockTodo

    const addTodo = (task) => {
        const newTodo = {
            id: Date.now(),
            isDone: false,
            task,
            createdDate: new Date().getTime(),
        }
        dispatch({ type: 'ADD', payload: newTodo })
    }

    // type : action 타입
    // payload : action에 전달할 값
    // 버튼 클릭 시 dispatch 함수를 이용하여 action을 발생시킵니다.
    // action.type에 따라 reducer 함수가 호출되어 state가 변경됩니다.
    // state가 변경되면 컴포넌트가 리렌더링되어 변경된 state를 반영하여 화면에 출력합니다.
    const onUpdate = (id) => {
        dispatch({ type: 'UPDATE', payload: id })
    }

    const onDelete = (id) => {
        dispatch({ type: 'DELETE', payload: id })
    }

    return (
        <div>
            <TodoHd />
            <TodoEditor addTodo={addTodo} />
            <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete} />
        </div>
    )
}

export default App
반응형
티스토리 친구하기