[React]복잡한 상태관리 로직 분리하기- useReducer

Hyoyoung Kim·2022년 8월 21일
0

React TIL

목록 보기
33/40

😎 복잡한 상태관리 로직을 컴포넌트로부터 분리하기

useReducer

컴포넌트에서 상태변화 로직을 분리

const [<상태 객체>, <dispatch 함수>] 
	= useReducer(<reducer 함수>, <초기 상태>, <초기 함수>)

<상태 객체> : 현재 state라고 할 수 있다.
<dispatch 함수> : 상태를 변화시키는 액션을 발생시키는 함수(상태변화를 일으킴)
<reducer 함수> : <dispatch 함수>가 일으킨 상태변화를 처리해주는 역할을 한다. discpatch함수가 호출이 되었을 때 reducer함수가 호출된다.
<초기 상태> : <상태 객체>의 초기값

예시를 보고 설명

<button onClick={()=> dispatch({type : 1})}>add 1</ button>

✔ 'add 1'이라는 버튼을 누르면 dispatch함수를 호출하면서 객체를 전달하게 된다.
✔ 그 객체{type : 1}는 꼭 type이라는 프로포티가 들어있다.
✔ dispatch함수가 같이 전달되는 이 객체{type : 1}를 'Action 객체(상태변화를 설명할 객체)'라고 한다.

reducer 함수

reducer 함수의 첫번재 인자(state): 상태변화가 일어나기 직전의 state값이다.
예시에서는 초기값인 1을 넣어주면 된다.

reducer 함수의 두번째 인자(action): 어떤 상태변화를 일으켜야 하는지의 대한 정보가 담긴 Action 객체가 담긴다.
예시에서는 dispatch함수의 Action 객체{type : 1}를 넣어준다.

✔ 상태관리를 처리하는 reducer 함수는 switch 케이스문을 활용해서 action의 type에 따라서 각각 다른 값을 반환한다. 반환된 값은 새로운 state 값이 된다.

👀 흐름
업로드중..


💪 useReduver을 이용해서 App컴포넌트에서 일기데이터인 'data state 상태변화로직'을 App컴포넌트에서 분리하는 작업을 하자

//App 컴포넌트

import logo from './logo.svg';
import { useEffect, useMemo, useRef,  useReducer} from 'react';
//1. useState를 주석처리하고 import한 것을 지우고 useReducer을 import해온다. 
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';
 
//reducer 함수를 만들어주는데 App 컴포넌트에서 분리하기 위해
//만든 것이기에 App컴포넌트 밖에서 만들어 준다. 

const reducer = (state, action)=> {
  switch(action.type){
    //여기서 리턴하는 값은 useReducer의 data값이 된다. 
    case 'INIT': {
      return action.data
      // action 객체에서 키가 data인 값의 value를 가져와 리턴해준다. 
      //dispatch({type : 'INIT', data: initData})
      //여기서 action.data의 값은 initData이기에 리턴값은 initData이다.
    }
    case 'CREATE': {
      const create_date = new Date().getTime();
      const newItem = {
        ...action.data,
        create_date
      }
      return [newItem, ...state]
      // 일기 리스트에 새로운 일기 데이터를 추가해주고 전에 원래
      // 있었던 일기 데이터들을 spread연산자로 다시 넣어준다.
    }
    case 'REMOVE': {
      return state.filter((it)=>it.id !==action.targetId);
      //지금 현재 State(일기 데이터 배열)값을 filter메소드를 이용하여 
      //순회하여 그 데이터의 id가 targetId와 같지 않은 경우에만 
      //리턴되게 해준다. 
    }
    case 'EDIT': {
      return state.map((it)=> 
      it.id === action.targetId ? 
      {...it, content : action.newCountent} : it)
      //targetId와 똑같은 요소를 찾아준 후 그 요소의 값을 
      //newContent로 바꿔주고
      //나머지 요소들을 spread요소로 다시 돌려준다. 
    }
    default :
    return state;
    //default 일때는 상태를 변화시키지 않는다.
  }
}

function App() {

// const [data, setData]= useState([]);

const [data, dispatch] =useReducer(reducer, [])
//data라는 state에 어떤 값이 필요한가(초기화,추가,제거,수정)
//App컴포넌트에서 set데이터가 했었던 역활을 dispatch와 reducer에 나눠줘야 한다. 

const dataId = useRef(0)

//*INIT*/
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, 
    create_date : new Date().getTime(),
    id : dataId.current ++ 
  }
})

dispatch({type : 'INIT', data: initData})
//reducer은 action객체를 받는데 그 action의 type이 'INIT'이고 
// 그 action에 필요한 데이터는 iniData가 된다.
//setData의 역할을 reducer이 하니깐 아래 코드는 지워도 된다. 
// setData(initData);

}


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

//*CREATE*/
 const onCreate =(author,content, emotion)=> {

dispatch({type : 'CREATE', data: {
   author,
  content,
  emotion,
  id : dataId.current}})

  // const create_date = new Date().getTime()
  // const newItem = {
  //   author,
  //   content,
  //   emotion,
  //   create_date,
  //   id : dataId.current
  // }
  dataId.current +=1
  // setData([newItem, ...data])
}; 

//*EDIT*/
const onEdit = (targetId, newCountent) => {
  dispatch({type : 'EDIT', targetId, newCountent})
  //reducer 함수의 두번쨰 인자인 action에 targetId, newCountent가 전달된다. 


  // setData(
  //   data.map((it)=>
  //   it.id === targetId ?
  //    {...it, content:newCountent} 
  //    : it)
  // )
}

//*REMOVE*/
const onRemove = (targetId) => {
  dispatch({type : 'REMOVE', targetId})
  // reducer에게 어떤 아이디를 가진 데이터를 지워라는 
  //의미에서 targetId만 전달한다. 


  // const newDiaryList = data.filter((it)=>it.id !==targetId);
  // setData(newDiaryList); 
}


const getDiaryAnalysis = useMemo(() => {

  const goodCount = data.filter((it)=> it.emotion >=3).length;
  const badCount = data.length - goodCount;
  const goodRatio = (goodCount/ data.length) *100;

  return{goodCount, badCount, goodRatio};

},[data.length ])

const {goodCount, badCount, goodRatio} = getDiaryAnalysis;


  return (
    <div className="App">
      <DiaryEditor onCreate = {onCreate}/>
      <div>전체일기 : {data.length}</div>  
      <div>기분 좋은 일기 개수 : {goodCount}</div>  
      <div>기분 나쁜 일기 개수 : {badCount}</div>  
      <div>기분 좋은 일기 비율 : {goodRatio}</div>  
      <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList ={data}/>
    </div>
  );
}

export default App;

0개의 댓글