🥹

아좌잣 홧팅이닷!

토독토독..💻

개발새발🐶🐾🐥🐾/React

[React Project: 간단한 일기장(9)] 로직 분리하기 -useReducer

SU_VIN 2023. 1. 6. 21:02
반응형

 

 

 

한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 - 인프런 | 강의

개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응용은 길게 17시간 분량의 All-in-one 강의!, - 강의 소개 | 인프런...

www.inflearn.com

 

 

 

 

 

 

 

[React Project: 간단한 일기장(8)] 최적화3 - useCallback

한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 - 인프런 | 강의 개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응

su-vin25.tistory.com

이어서...

 


간단한 일기장 만들기는 최적화도 했으니 모두 완료가 되었다! 하지만 우리는 여기서 끝이 아니라

이 프로젝트를 좀 더 업그레이드시켜 볼 것이다

 

우리는 App.js 컴포넌트에

onCreate

onDelete

onEdit

이렇게 많은 상태변화처리함수를 가지고 있다 이 상태변화함수들은 컴포넌트 내에만 존재한다

상태를 업데이트하기 위해선 기존에 상태를 참조해야 하기 때문이다 

이렇게 컴포넌트에 함수가 많아지고 코드가 길어지는 건 좋지 않다 그래서 우리는 이렇게 복잡하고 긴 상태변화함수를

컴포넌트로 밖으로 분리해볼 것이다 여기서 쓰이는 기능이 useReducer이다


useReducer

React에서 컴포넌트의 상태 관리를 위해서 useState를 사용해서 상태를 업데이트를 하는데, useReducer를 사용하게 되면 
컴포넌트와 상태 업데이트 로직을 분리하여 컴포넌트 외부에서도 상태 관리를 할 수 있게 된다
const [state, dispatch] = useReducer(reducer, initialState);

state = 상태

dispatch= 상태를 변화시키는 action을 발생시키는 함수

reducer= dispatch로 인해 일어난 상태변화를 처리해 줌

initialState= state의 초기값

 

dispatch와 함께 전달되는 객체를 action 객체라고 한다

전달된 이 객체는 reducer에서 처리한다

 

우리는 이제 App컴포넌트의 일기 dataState를 useState가 아닌 useReducer로 관리해볼 것이다

const [data,setData]=useState([]); //x
const[data,dispatch]=useReducer(reducer,[]); //o

기존의 useState는 지워주자  

다음 useReducer를 쓰는 이유가 컴포넌트에서 분리하기 위함이므로 App컴포넌트 바깥에 reducer 함수를 생성해 주자

 

const reducer=(state,action)=>{
  switch(action.type){

  }
}

매개변수로 상태와 dispatch객체의 action을 받아 그 action의 type으로 switch문을 돌려 행동한다

 

그럼 data의 state에 어떤 action들이 필요한지 찾아보자

const reducer=(state,action)=>{
  switch(action.type){
    case 'INIT':
    case 'CREATE':  
    case 'DELETE':
    case 'EDIT': 
    default:
  }
}

이렇게 총 4개의 action들이 필요하다

 

그 후 지금까지 setData가 해오던 일들을 Dispatch와 reducer에게 적절히 나누어주도록 하자

 

 

우선 init을 하는 getData함수로 가보자

setData(initData);   //x
dispatch({type:"INIT",data:initData}); //o

setData에 initData를 보내주는 대신

dispatch를 써준다

그럼 reducer에서는 dispatch의 action객체를 받는데 type은 INIT이고 그 action에 필요한 data는 initData라는 걸 알 수 있다

 

case 'INIT':{
  return action.data
}

그 후 reducer에서 INIT 케이스에서 action의 data를 리턴해주면 된다

 

모두 같은 방식으로 고쳐주자

 

  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 onDelete = useCallback((targetId)=>{
    dispatch({
      type:"DELETE",
      targetId
    })
  },[])



  const onEdit = useCallback((targetId,newContent)=>{
    dispatch({
      type:"EDIT",
      targetId,
      newContent
    })
  },[])
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 'DELETE':{
      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;
  }
}

실행해보면 똑같이 동작하는 걸 볼 수 있다

반응형