Front-end/React

[React] React Hooks : useReducer

개발중인제이 2023. 3. 3. 10:21

 

# useReducer

 

  • useState 처럼 리액트의 상태관리를 돕는 도구
  • 복잡한 상태변화 로직을 컴포넌트에서 분리
  • 복잡한 코드를 깔끔하게 정리할 수 있어서 유지보수에 좋음
  • Readucer / Dispatch / Action 으로 이루어져 있다.

 

예를들어 우리가 은행거래를 할 때를 생각하면 쉽다. 

 

 

1. 철수가 은행에서 거래를 하기위해 요구(Dispatch)를 한다.

2. 그 요구에는 '만원을 출금해주세요' 라는 내용(action)이 있다. 

3. 그리고나서 은행(Reducer)에 전달을 한다. 

4. 은행은 내용대로 처리를 한뒤 철수의 은행거래내역(state)에 업데이트를 해준다. 

 

  • reducer : state를 업데이트 하는 역할(은행)
  • dispatch : state 업데이트를 위한 요구
  • action : 요구의 내용 

 


# 예제 1 

 

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

const ACTION_TYPES = {
   deposit: 'deposit',
   withdraw: 'withdraw',
}

const reducer = (state, action) => {
   switch (action.type){
      case ACTION_TYPES.deposit : 
         return state + action.payload;
      case ACTION_TYPES.withdraw :
         return state - action.payload;
      default:
         return state;
   }
};


function App() { 
   const [number, setNumber] = useState(0); 
   const [money, dispatch] = useReducer(reducer, 0);

   return (
      <div>
         <h2>useReducer 은행에 오신것을 환영합니다.</h2>
         <p>잔고: {money}원</p>
         <input 
            type='number'
            value={number}
            onChange={(e) => setNumber(parseInt(e.target.value))}
            step='1000'
         />
         <button onClick={() => {
            dispatch({type: ACTION_TYPES.deposit, payload: number});
         }}>예금</button>
         <button onClick={() => {
            dispatch({type: ACTION_TYPES.withdraw, payload: number});
         }}>출금</button>
      </div>
   );
}

export default App;

 

  • dispatch에 action을 전달할 때 그안에는 보통 {} 객체 형태로 전달하고 type을 적어준다.
  • type 안에는 요구를 적어준다. 
  • reducer가 리턴하는 값이 새로 업데이트되는 값이다. 
  • 보통 reducer 안에는 if..else문, switch..case문을 사용한다. 
  • reducer의 장점은 전달받은 action 대로만 state를 업데이트 시켜준다. 

 


# 예제 2

 

 

import React, { useReducer, useState } from 'react';
import Student from './Student';

const reducer = (state, action) => {
   switch(action.type){
      case 'add-student': 
         const name = action.payload.name;
         const newStudent = {
            id: Date.now(),
            name,
            isHere: false,
         }
         return {
            count: state.count + 1,
            students: [...state.students, newStudent],
         }
      case 'delete-student':
         return {
            count: state.count - 1,
            students: state.students.filter(student => student.id !== action.payload.id),
         }
      case 'mark-student':
         return {
            count: state.count,
            students: state.students.map(student => {
               if(student.id === action.payload.id){
                  return {...student, isHere: !student.isHere}
               }
               return student;
            })}
      default:
         return state;
   }
}

const initialState = {
   count : 0,
   students: [],
}

function App() { 
   const [name, setName] = useState('');
   const [studentInfo, dispatch] = useReducer(reducer, initialState)

   return (
      <div>
         <h1>출석부</h1>
         <p>총 학생 수: {studentInfo.count}</p>
         <input 
            type="text" 
            placeholder='이름을 입력해주세요'
            value={name}
            onChange={(e) => setName(e.target.value)}
         />
         <button onClick={() => {
            dispatch({type: 'add-student', payload: {name}})
         }}>추가</button>
         {studentInfo.students.map((student) => {
            return (
               <Student 
                  key={student.id} 
                  name={student.name} 
                  dispatch={dispatch} 
                  id={student.id}
                  isHere={student.isHere}
               />
            );
         })}
      </div>
   );
}

export default App;

 

import React from 'react';

const Student = ({name, dispatch, id, isHere}) => {
    return(
        <div>
            <span 
                style={{
                    textDecoration: isHere ? 'line-through' : 'none',
                    color: isHere ? 'gray' : 'black',
                }}
                onClick={() => {
                    dispatch({type: 'mark-student', payload: {id}})
                }}
            >{name}</span>
            <button onClick={() => {
                dispatch({type: 'delete-student', payload: {id}})
            }}>삭제</button>
        </div>
    )
}

export default Student;

 

 


 

 

📌 참고
별코딩 | 리액트 훅스 시리즈