[간단 일기장] 복잡한 상태 관리 로직 분리하기- useReducer

미아·2023년 1월 6일
0

REACT

목록 보기
24/41


=> app컴포넌트는 코드의 길이가 길고, data를 계속 참조해서 써야하므로 함수 안에 onCreate, onEdit, onRemove가 들어가있어야했다!

useReducer 이용하면 컴포넌트 가볍게 만들 수 있다!

  • 순서 정리
  1. useReducer 사용해서 count라는 state만들면, 초기값은 1로 할당
  2. 처음 1인 count state값 변경하고 싶으면 상태변화함수인 dispatch(useState에서의 setState같다고 생각하면 됨) 호출해서 상태 변화 일으키면 상태변화처리함수인 reduceer가 그걸 처리하게 되는것
  3. reducer로 전달한 action 객체는 dispatch가 일어나면 switch-case문 이용해서 action.type에 따라 새로운 state 반환

상태 변화 함수로부터 독립시키기

  • useReducer와 함께 data state의 상태 변화 로직 분리!
import { useMemo, useEffect, useRef, useCallback, useReducer } from "react";
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
//복잡한 컴포넌트 로직을 간단하게 바꿔주기 위해서이므로, 밖에 선언! 3️⃣
const reducer = (state, action) => {
  //언제나 두개의 매개변수 받아야!

  //reducer함수에서 데이터 가공한다고 생각하자!
  //dispatch부르는 부분에서는 전달만!
  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      const created_date = new Date().getTime(); //이제 더이상 onCreate에서 받아오지않음
      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 } //id 일치하면 나머지 펼쳐주고, 수정한애만(content) newcontent로 바꾸기
          : it
      );
    }
    default:
      //reducer에는 상태 변화시킬 의무, 즉 return 필요
      return state;
  }
  //reducer가 리턴하는 값이 data의 값이 된다.
  //어떤 data의 값이 필요한지 둘러보기4️⃣
};

function App() {
  // const [data, setData] = useState([]); 1️⃣ 주석처리
  // 2️⃣useReducer만들어주고
  const [data, dispatch] = useReducer(reducer, []); //dispatch= setState같은애인데 항상 dispatch라고 이름지어줘야함
  //dispatch를 호출하면 reducer가 실행된다.
  // 초기값은 useState때처럼 [] 빈배열!

  const dataId = useRef(0);

  const getData = async () => {
    const url = "https://jsonplaceholder.typicode.com/comments";
    const res = await fetch(url).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(),
        //return바로 되어서 후에 +1할수없으니까
        id: dataId.current++,
      };
    });
    //res배열 500개여서 20개만 자른다(0~19) - slice
    //Math.random() * 5 => 0~4까지 랜덤난수 생성 , floor이용해서 내림 +1 (1~5까지)
    dispatch({ type: "INIT", data: initData });
    //type으로 알아보니까 담아주고, 데이터를 또 다른 프로퍼티로 담아준다.
  };

  //실행할 시점: app 컴포넌트가 마운트 되자마자!
  useEffect(() => {
    getData();
  }, []);

  // 전달해줄 함수 onCreate
  const onCreate = useCallback((author, content, emotion) => {
    dispatch({
      type: "CREATE",
      data: { author, content, emotion, id: dataId.current },
    });
    // const created_date = new Date().getTime();
    // const newItem = {
    //   author,
    //   content,
    //   emotion,
    //   created_date,
    //   id: dataId.current,
    // }; 이거 대신, dispatch로 바로 데이터 받아서 가공해주게!
    dataId.current += 1; //current에 1 더해줘야함
  }, []);

  // 삭제해줄 함수 onRemove , 어떤 아이디를 갖고있는 애를 지우면 됨!
  const onRemove = useCallback((targetId) => {
    dispatch({ type: "REMOVE", targetId }); //id만 전달해주면 reducer함수에서 가공
  }, []); // 얘를 지우려면, DiaryItem에 가서 지워야함(컴포넌트 전달은 부모인 DiaryList에게!)

  // 수정해줄 함수 onEdit
  const onEdit = useCallback((targetId, newContent) => {
    dispatch({ type: "EDIT", targetId, newContent });
  }, []);

  // useMemo실습: 좋은감정, 안좋은감정 나누기
  const getDiaryAnalysis = useMemo(() => {
    //좋은 일기 개수
    const goodCount = data.filter((it) => it.emotion >= 3).length;
    //안좋은 일기 개수
    const badCount = data.length - goodCount;
    //좋은 일기 비율
    const goodCountRatio = (goodCount / data.length) * 100;
    //전체일기를 화면에 찍어줄것임 => return필요, 여러개니까 객체로 리턴
    return { goodCount, badCount, goodCountRatio };
  }, [data.length]); //두번째 인자 배열에 전달한 값이 바뀔때만 다시 수행함!
  const { goodCount, badCount, goodCountRatio } = getDiaryAnalysis; //비구조화할당 이용해서 리턴값 변수에 담아주고(키로 구분), 함수 호출
  return (
    <div className="App">
      <DiaryEditor onCreate={onCreate} />
      <div>전체일기:{data.length}</div>
      <div>기분 좋은 일기 개수:{goodCount}</div>
      <div>기분 나쁜 일기 개수:{badCount}</div>
      <div>기분 좋은 일기 비율:{goodCountRatio}</div>
      <DiaryList onRemove={onRemove} diaryList={data} onEdit={onEdit} />
      {/* prop으로 전달함!(부모=> 자식컴포넌트로 데이터 전달!) */}
    </div>
  );
}

export default App;
  • dispatch는 함수형 업데이트와 상관없이 그냥 호출하면 알아서 현재 state 를 reduce함수가 참조해서 자동으로 업데이트하므로 useCallback이용하면서 dependency array를 걱정하지 않아도 된다.
profile
새로운 것은 언제나 재밌어 🎶

0개의 댓글