DiaryApp - useReducer()를 활용한 상태함수 관리

dobyming·2023년 2월 6일
0

React Study

목록 보기
9/13
post-thumbnail

App.js 컴포넌트의 상태함수 관리하기

🔬 지금까지의 데이터 flow

React의 단방향성 데이터 흐름이라는 특징으로 인해, App 컴포넌트에서 useState로 state를 정의하고, 각각의 컴포넌트에 데이터(state)를 흘려보내기 위해 stateful한 함수로 정의하여 props를 전달하는 형식으로 state를 끌어올리며 작업을 수행했습니다.

const App = () => {
  const [data,setData] = useState([]); 
  return ..
}

물론 최적화까지 다 끝낸 코드기 때문에 작동상에는 문제는 없지만, App 컴포넌트에 많은 상태함수를 정의했기에 후에 코드 유지보수에 어려움을 겪을 수 있습니다.


🪓 useReducer로 상태변화 함수를 컴포넌트에서 분리

App 컴포넌트 파일에 여러 개의 상태변화 함수를 나열했던 부분을 컴포넌트에서 분리할 수 있는 useReducer() 리액트 훅을 통해 리팩토링을 수행할 수 있습니다.

useReducer Doc 에서도 첫번째 줄부터 명시되어 있듯이, useState()를 대체할 수 있는 함수라고 명시 되어있습니다.

useReducer 사용법

1. App 컴포넌트에 useReducer 사용하기

const [count,dispatch] = useReducer(reducer,1);
  • count : useReducer 함수의 2번째 매개변수가 count의 초기값
  • dispatch : state를 raise하여 App 컴포넌트 밖에 정의된 reducer함수에 전달

2. reducer() 메소드 정의하기

상태변화 함수를 App 컴포넌트 내에서 분리하기 위해, 해당 useReducer() 메소드는 전역적으로 정의합니다.

const reducer = (state,action) => {
	switch(action.type){
		case 1:{
			return state + 1;
        }
        default:{
          return state;
        }
    }
};

reducer 함수는 action에 기반하여 상태변화 로직을 처리합니다.

  • state: 이전에 저장된 상태
  • action:switch문에 해당하는 action 처리

👩🏼‍💻 Let's Refactoring the code

기존에setState()로 정의된 부분을 dispatch로 state를 raise하여 reducer 함수에 전달하는 로직으로 리팩토링 할 수 있습니다.

그럼 제 프로젝트에서 일기 생성과 삭제 부분에 대한 리팩토링을 한번 다뤄보도록 하겠습니다.

reducer() 함수 정의하기

-App.jsuseReducer() 선언하기

const [data,dispatch] = useReducer(reducer,[]);

data는 초기값으로 빈배열로 setting하면서 state를 받고, dispatch함수는 reducer 함수를 raise할 것입니다.

-App 컴포넌트 밖에다가 reducer() 함수 정의하기
생성과 삭제에 해당하는 action에 대해서 reducer함수를 mock-up 시 다음과 같습니다.

const reducer = (state,action) => {
  switch(action.type) {
    case 'CREATE':{ //생성
    }
    case 'REMOVE':{ //삭제 
    }
    default:
      return state;
  }
};

그럼 이제 어느정도 1차 setting까지 마쳤습니다.

1. onCreate() 함수 Refactoring

Before

  //일기 생성 기능
  const onCreate = useCallback(
    (author,content,emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      author,
      content,
      emotion,
      created_date,
      id: dataId.current
    }
    dataId.current += 1;
    setData((data)=>[newItem,...data]);
  },[]);

위 코드에서는 setData()useReducer()에서 dispatch에 해당하는 코드로 볼 수 있습니다. 따라서 위의 코드를 아래와 같이 리팩토링 할 수 있습니다.

After

  const onCreate = useCallback(
    (author,content,emotion) => {
    dispatch({
      type:'CREATE',
      data:{author,content,emotion, id:dataId.current }
    });
    dataId.current += 1;
  },[]); 

dispatch()함수는 state를 raise(=action)하여 reducer()함수에 전달하는 역할을 합니다. 따라서, 매개변수를 객체 형태로 typedatareducer()함수에 전달합니다.

이 말은 곧, 구체적인 state handle은 reducer함수의 case문 내에 정의한다는 것을 의미합니다. 그럼 다시 reducer() 함수로 되돌아가 정의해봅시다.

reducer()함수에 CREATE action 정의하기

const reducer = (state,action) => {
  switch(action.type) {
    case 'CREATE':{
      const created_date = new Date().getTime();
      const newItem = {
        ...action.data, //onCreate에서 setting한 값 spread
        created_date
      }
      return [newItem,...state]; 
    }
    case 'REMOVE':{ //삭제 
    }
    default:
      return state;
  }
};

dispatch함수에서 data로 받아온 값들을 action에 전달하여 spread하여 newItem 객체로 감싸고, 그리고 return 시에 배열에 담아서 최종적으로 리턴합니다.

2. onRemove() 함수 Refactoring

onCreate()와 동일한 방식으로 onRemove()함수도 다음과 같이 dispatch 함수로 state를 raise하는 코드로 작성할 수 있습니다.

  const onRemove = useCallback((targetId) => {
    dispatch({type:'REMOVE',targetId});
  },[]);

targetId를 기준으로 data를 삭제하기 때문에, data에 targetId를 담아서 state를 raise합니다.

reducer()함수에 REMOVE action 정의하기

const reducer = (state,action) => {
  switch(action.type) {
    case 'CREATE':{
      const created_date = new Date().getTime();
      const newItem = {
        ...action.data, //onCreate에서 setting한 값 spread
        created_date
      }
      return [newItem,...state]; 
    }
    case 'REMOVE':{
      return state.filter((it)=> it.id !== action.targetId);
    }
    default:
      return state;
  }
};

dispatch()로 raise한 action에서의 targetId와 일치하지 않는것만 현재 state로 반환하게 코드를 구현할 수 있다.

정리

이와 같이 리액트 훅 중 하나인 useReducer()를 활용하여 단방향성의 데이터 흐름을 지닌 React를 위해 선언한 여러개의 상태관리 함수를 깔끔히 리팩토링 할 수 있었다.

0개의 댓글