useReducer

Jaeseok Han·2023년 11월 10일
0

React Basic

목록 보기
24/30
post-thumbnail

useReducer

useState에서 배열을 사용하는 학습 선행 필요
https://velog.io/@wotjr294/useState-Object

Challenge

  • 리셋 기능 추가
    데이터 배열에 있는 값을 사용하여 리셋 기능 및 상태를 초기화하는 함수를 생성
  • 버튼 추가
    클리어 버튼과 비슷한 역할을 하는 리셋 버튼 추가
  • 조건부 렌더링
    'people' 값에 따라 버튼을 토글하는데 'people' 값이 있으면 클리어 버튼을 없으면 리셋 버튼을 표시
import React from 'react';
import { data } from '../../../data';
const ReducerBasics = () => {
  const [people, setPeople] = React.useState(data);

  const removeItem = (id) => {
    let newPeople = people.filter((person) => person.id !== id);
    setPeople(newPeople);
  };

  const clearList = () => {
    setPeople([]);
  }

  const resetList = () => {
    setPeople(data);
  }

  return (
    <div>
      {people.map((person) => {
        const { id, name } = person;
        return (
          <div key={id} className='item'>
            <h4>{name}</h4>
            <button onClick={() => removeItem(id)}>remove</button>
          </div>
        );
      })}
      {people.length < 1 ? 
          <button
            className='btn'
            style={{ marginTop: '2rem' }}
            onClick={resetList}
          >
            Reset
          </button>
        :
        <button
          className='btn'
          style={{ marginTop: '2rem' }}
          onClick={clearList}
        >
          Clear
        </button>
      }
    </div>
  );
};

export default ReducerBasics;

Remove useState

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

const defaultState = {
  people : data,
  isLoading : false,
}

const reducer = (state, action) => {
  if(action.type === 'CLEAR_LIST'){
    return {...state, people : []};
  }
}

const ReducerBasics = () => {

  const [state, dispatch] = useReducer(reducer, defaultState)

  const removeItem = (id) => {
    // let newPeople = people.filter((person) => person.id !== id);
    // setPeople(newPeople);
  };

  const clearList = () => {
    dispatch({ type : 'CLEAR_LIST'});
    // setPeople([]);
  }

  const resetList = () => {
    // setPeople(data);
  }
  console.log(state)
  return (
    <div>
      {state.people.map((person) => {
        const { id, name } = person;
        return (
          <div key={id} className='item'>
            <h4>{name}</h4>
            <button onClick={() => removeItem(id)}>remove</button>
          </div>
        );
      })}
      {state.people.length < 1 ? 
          <button
            className='btn'
            style={{ marginTop: '2rem' }}
            onClick={resetList}
          >
            Reset
          </button>
        :
        <button
          className='btn'
          style={{ marginTop: '2rem' }}
          onClick={clearList}
        >
          Clear
        </button>
      }
    </div>
  );
};

export default ReducerBasics

상태관리

  • useReducer 훅을 사용하여 상태가 state로 관리된다.

action dispatch

  • 액션은 dispatch 함수를 사용하여 디스패치되며 액션을 처리하는 로직은 reducer 함수에서 정의된다.

리듀서 로직

  • 액션(CLEAR_LIST)에 대한 처리로 상태값이 변한다.

💡 useReducer의 주요 개념

  • State 와 Dispatch
    useReducer는 두 가지 값을 반환한다. 첫 번째는 현재 상태(state), 두 번째는 상태를 업데이트하는 함수(dispatch)이다.
  • 초기 상태와 리듀서 함수
    useReducer 를 사용할 때는 초기 상태와 리듀서 함수를 함께 제공한다. 초기 상태는 상태의 초기값을 나타내며, 리듀서 함수는 상태를 어떻게 업데이트할지에 대한 로직을 담고 있는 함수이다.
  • 액션(Action)
    dispatch 함수를 호출할 때 사용되는 객체로, 최소한 type속성을 가지고 있어야 한다. type은 어떤 종류의 액션을 수행할 것인지를 나타낸다.
  • 리듀서 함수
    현재 상태(state)와 액션(action)을 받아서 새로운 상태를 반환하는 함수이다. 액션의 타입에 따라 다양한 로직을 수행할 수 있다.
const [state, dispatch] = useReducer(reducer, initialState);

Action and Default State

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

const defaultState = {
  people : data,
  isLoading : false,
}

const CLEAR_LIST = 'CLEAR_LIST';
const RESET_LIST = 'RESET_LIST';
const REMOVE_LIST = 'REMOVE_ITEM';

const reducer = (state, action) => {
  if(action.type === CLEAR_LIST){
    return {...state, people : []};
  } 
  if (action.type === RESET_LIST){
    return {...state, people : data}
  }

  throw new Error(`No matching "${action.type}" - action type`)
}

const ReducerBasics = () => {

  const [state, dispatch] = useReducer(reducer, defaultState)

  const removeItem = (id) => {
    // let newPeople = people.filter((person) => person.id !== id);
    // setPeople(newPeople);
  };

  const clearList = () => {
    dispatch({ type : CLEAR_LIST });
  }

  const resetList = () => {
    dispatch({type : RESET_LIST})
  }
  console.log(state)
  return (
    <div>
      {state.people.map((person) => {
        const { id, name } = person;
        return (
          <div key={id} className='item'>
            <h4>{name}</h4>
            <button onClick={() => removeItem(id)}>remove</button>
          </div>
        );
      })}
      {state.people.length < 1 ? 
          <button
            className='btn'
            style={{ marginTop: '2rem' }}
            onClick={resetList}
          >
            Reset
          </button>
        :
        <button
          className='btn'
          style={{ marginTop: '2rem' }}
          onClick={clearList}
        >
          Clear
        </button>
      }
    </div>
  );
};

export default ReducerBasics;

액션 type 값을 전역 변수로 지정하여 실수를 방지하고, reducer를 처리하는 함수에서 type값에 대한 상태 관리 처리가이루어지지 않은 경우 에러처리를 하였다

Reset List Challenge

  • reducer에 dispatch 와 action 설정
import { useState, useReducer } from 'react';
import { data } from '../../../data';

const defaultState = {
  people : data,
  isLoading : false,
}

const CLEAR_LIST = 'CLEAR_LIST';
const RESET_LIST = 'RESET_LIST';
const REMOVE_ITEM = 'REMOVE_ITEM';

const reducer = (state, action) => {
  if(action.type === CLEAR_LIST){
    return {...state, people : []};
  } 
  if (action.type === RESET_LIST){
    return {...state, people : data}
  }
  if (action.type === REMOVE_ITEM){
    let newPeople = state.people.filter((person) => person.id !== action.payload.id);
    return {...state, people : newPeople};
  }

  throw new Error(`No matching "${action.type}" - action type`)
}

const ReducerBasics = () => {

  const [state, dispatch] = useReducer(reducer, defaultState)

  const removeItem = (id) => {
    dispatch({type : REMOVE_ITEM, payload: {id}})
  };

  const clearList = () => {
    dispatch({ type : CLEAR_LIST });
  }

  const resetList = () => {
    dispatch({type : RESET_LIST})
  }
  console.log(state)
  return (
    <div>
      {state.people.map((person) => {
        const { id, name } = person;
        return (
          <div key={id} className='item'>
            <h4>{name}</h4>
            <button onClick={() => removeItem(id)}>remove</button>
          </div>
        );
      })}
      {state.people.length < 1 ? 
          <button
            className='btn'
            style={{ marginTop: '2rem' }}
            onClick={resetList}
          >
            Reset
          </button>
        :
        <button
          className='btn'
          style={{ marginTop: '2rem' }}
          onClick={clearList}
        >
          Clear
        </button>
      }
    </div>
  );
};

export default ReducerBasics;

Reducer 과정

1. 초기 설정

useReducer 함수 설정

const initialState = {
  // 초기 상태 정의
};

const reducer = (state, action) => {
  // 액션에 따른 상태 업데이트 로직 정의
};

2. 상태 및 디스패치 생성

useReducer 를 호출하여 현재 상태(state)와 액션을 디스패치하는 함수(dispatch)를 생성

const [state, dispatch] = useReducer(reducer, initialState);

3. 액션 생성 및 디스패치

액션 객체를 생성하고 dispatch 함수를 사용하여 리듀서에게 액션을 전달

const action = {
  type: 'SOME_ACTION',
  payload: 'some data',
};
dispatch(action);

4. 리듀서 처리

리듀서 함수에서 액션의 type을 기반으로 상태를 업데이트하는 로직을 작성

const reducer = (state, action) => {
  switch (action.type) {
    case 'SOME_ACTION':
      // 액션 타입에 따른 상태 업데이트 로직
      return { ...state, someProperty: action.payload };
    // 다른 액션에 대한 처리...
    default:
      return state;
  }
};

5. 상태 갱신

리듀서에서 반횐된 새로운 상태가 useReducer에 의해 현재 상태로 갱신

const [state, dispatch] = useReducer(reducer, initialState);

Import / Export

  • action.js 생성
    모든 action 복사
    export/import action

  • reducer.js 생성
    import action
    import data
    export/import reducer

import { useState, useReducer } from 'react';
import { data } from '../../../data';
import { CLEAR_LIST, RESET_LIST, REMOVE_ITEM } from './actions';
import reducer from './reducer';


const defaultState = {
  people : data,
  isLoading : false,
}


const ReducerBasics = () => {

  const [state, dispatch] = useReducer(reducer, defaultState)

  const removeItem = (id) => {
    dispatch({type : REMOVE_ITEM, payload: {id}})
  };

  const clearList = () => {
    dispatch({ type : CLEAR_LIST });
  }

  const resetList = () => {
    dispatch({type : RESET_LIST})
  }
  console.log(state)
  return (
    <div>
      {state.people.map((person) => {
        const { id, name } = person;
        return (
          <div key={id} className='item'>
            <h4>{name}</h4>
            <button onClick={() => removeItem(id)}>remove</button>
          </div>
        );
      })}
      {state.people.length < 1 ? 
          <button
            className='btn'
            style={{ marginTop: '2rem' }}
            onClick={resetList}
          >
            Reset
          </button>
        :
        <button
          className='btn'
          style={{ marginTop: '2rem' }}
          onClick={clearList}
        >
          Clear
        </button>
      }
    </div>
  );
};

export default ReducerBasics;

actions.js

export const CLEAR_LIST = 'CLEAR_LIST';
export const RESET_LIST = 'RESET_LIST';
export const REMOVE_ITEM = 'REMOVE_ITEM';

reducer.js

import { CLEAR_LIST, RESET_LIST, REMOVE_ITEM } from './actions';
import { data } from '../../../data';

const reducer = (state, action) => {
    if(action.type === CLEAR_LIST){
      return {...state, people : []};
    } 
    if (action.type === RESET_LIST){
      return {...state, people : data}
    }
    if (action.type === REMOVE_ITEM){
      let newPeople = state.people.filter((person) => person.id !== action.payload.id);
      return {...state, people : newPeople};
    }
  
    throw new Error(`No matching "${action.type}" - action type`)
}

export default reducer;

0개의 댓글