반응형
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

복잡한 상태 관리 로직 분리하기 본문

인프런, 유데미/한입 크기로 잘라 먹는 리액트

복잡한 상태 관리 로직 분리하기

5_hyun 2022. 5. 30. 14:00
반응형

App 컴포넌트에는 굉장히 많은 상태들이 존재한다.

 

-상태 변화 처리 함수

onCreate: 데이터 생성

onEdit: 데이터 수정

onRemove: 데이터 삭제

이 3개의 상태 변화 로직이 있다.

함수들 안에 data가 많다. 이렇게 컴포넌트의 코드가 길어지고 무거워 지는 것은 좋지 않다.

=> 따라서 복잡하고 긴 상태 변화 로직을 컴포넌트 밖으로 분리해야한다.

 

useReducer

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

이렇게 있으면 왼쪽 첫번째 인자는 state이다. 그리고 두번째 인자는 상태를 변환시키는 엑션을 발생시키는 함수이다.

오른쪽 첫번째 인자에는 reducer가 있어야한다. dispatch에서 일어난 상태 변화를 reducer가 처리해준다. 그리고 두번째 인자는 초기값이다.

 

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

버튼을 누르면 dispatch가 호출이된다. 그러면서 type 객체를 전달한다. 이 객체를 action 객체라고 한다.

action은 상태 변화라고 생각하면 된다.

따라서 dispatch가 호출되면 상태변화가 일어나고 이 상태 변화는 reducer가 처리한다.

 

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

dispatch가 일어나면 첫번째 인자로 최신의 state를 받고, 두번째 인자로는 dispatch 안의 type인 action 객체를 받는다.

 

-최적화 한 App.js

const reducer = (state, action) => {
  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      const created_date = new Date().getTime();
      const newItem = {
        ...action.data,
        created_date,
      };
      return [newItem, ...state];
    }
    case "REMOVE": {
      return state.filter((it) => it.id !== action.targetId);
    }
    case "EDIT": {
      return state.map((it) =>
        it.id === action.targetId ? { ...it, content: action.newContent } : it
      );
    }
    default:
      return state;
  }
};

const App = () => {
  // const [data, setData] = useState([]);
  const [data, dispatch] = useReducer(reducer, []);

  const dataId = useRef(0);

  const getData = async () => {
    const res = await fetch(
      "https://jsonplaceholder.typicode.com/comments"
    ).then((res) => res.json());

    const initData = res.slice(0, 20).map((it) => {
      return {
        author: it.email,
        content: it.body,
        emotion: Math.floor(Math.random() * 5) + 1,
        created_date: new Date().getTime(),
        id: dataId.current++,
      };
    });
    dispatch({ type: "INIT", data: initData });
  };

  useEffect(() => {
    getData();
  }, []);

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

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

  const onEdit = useCallback((targetId, newContent) => {
    dispatch({ type: "EDIT", targetId, newContent });
  }, []);

이 코드를 보면 App 컴포넌트 외부에 reducer 함수를 만들어 컴포넌트의 길이와 복잡도를 줄인 것이 핵심이다.

dispatch는 함수형 업데이트 필요없이 그냥 호출하면 알아서 현재의 state를 reducer 함수가 참조해줘서 useCallback을 사용하면서 dependency array를 걱정하지 않아도 된다.

반응형
Comments