[React]-state의 update도 따로 관리하자! useReducer

badassong·2023년 4월 6일
0

React

목록 보기
31/56
post-thumbnail

컴포넌트 내부에 객체를 업데이트 하는 로직이 섞여있고, 객체를 변경하는 로직들을 다른 컴포넌트에서 쓸 수가 없을 때, 다른 컴포넌트에서도 그 로직을 그대로 사용하고 싶을 때 useReducer를 사용한다!

계속 useState만 써왔는데 useReducer 뭐야,, 처음 배운 hook이라 너무 낯설다,,ㅜ

person-reducer.js

export default function personReducer(prev, action) {
  switch (action.type) {
    case "updated": {
      const { change, current } = action;
      // const change = action.change;  // 이 두줄을 축약한 코드가 위에 코드!(구조분해할당)
      // const current = action.current;
      return {
        ...prev,
        mentors: prev.mentors.map((mentor) => {
          if (mentor.name !== change) {
            return mentor;
          } else {
            return { ...mentor, name: current };
          }
        }),
      };
    }
    case "added": {
      const { name, title } = action;
      return {
        ...prev,
        mentors: [...prev.mentors, { name, title }],
      };
    }
    case "deleted": {
      //   const { deleteMentor } = action;
      return {
        ...prev,
        mentors: prev.mentors.filter(
          (mentor) => mentor.name !== action.deleteMentor
        ),
      };
    }
    default: {
      throw Error(`알 수 없는 액션 타입이다: ${action.type}`);
    }
  }
}

이게 바로 state를 업데이트 하는 로직 reducer이다!
personReducer의 첫번째 파라미터는 기존의 받아올 객체, 두번째 파라미터는 그 객체의 상태가 어떻게 변하길 원하는지 action을 받아온다.
그리고 전달받은 기존의 prev라는 객체를 업뎃했는지, 삭제했는지, 추가했는지를 판단해서 각 해당하는 액션에 필요한 데이터를 받아온다!
이런 액션에 따라서 새로운 prev객체를 만들어서 리턴해주는 함수를 만드는 것이다!

예를 들어 case가 updated라면, 이전의 값은 무엇이고(prev), 현재 값(current)은 무엇인지 받아온다!

그리고 만약 핸들링하지 않은 액션이라면, 즉 default로 넘어왔을 때 각 case에 해당하지 않는다면 에러를 던져줘야 한다! 이렇게 에러를 던져줘야 내가 코드를 짤 때 만약 알 수 없는 액션을 전달하면, 에러가 발생해서 내가 코드를 잘못 짜고 있다는걸 알 수 있다!

그런데 여기서 왜 case가 deleted일 때만

const { deleteMentor } = action;

이 데이터 받아오는 코드를 생략한건지 궁금했다. 알아보니,

const { change, current } = action;
const { name, title } = action;

보통 이렇게 사용할 경우는 action 객체 안에서 여러개의 데이터를 꺼내서 구조분해할당을 사용해야 할 경우이다.

하지만 deleted에서는 deleteMentor 하나만 사용하기 때문에 굳이 구조분해할당을 할 필요가 없어서 단순하게 사용한 것 같다. 사실 두 경우가 그렇게 큰 의미를 둘 정도로 차이가 있진 않다고 한다!

자, 이렇게 reducer.js파일을 만들어줬으면 이제 useState는 필요가 없어졌다!

const [person, setPerson] = useState(initialPerson);

대신에

const [person, dispatch] = useReducer(personReducer, initialPerson);

이 한줄을 추가해준다!
useReducer는 내가 객체를 새롭게 만들어나갈 로직을 작성한 함수를 전달해주고(personReducer), 초기값(initialPerson)을 전달해주면
상태에 접근할 수 있는 변수(person)와 reducer를 호출할 수 있는 dispatch가 있다. 이 dispatch를 통해
우리가 원하는 action을 명령할 수 있다!

const handleUpdate = () => {
    const change = prompt(`누구의 이름을 바꾸고 싶은가요?`);
    const same = person.mentors.filter((mentor) => {
      return mentor.name == change;
    });
    // console.log(same);
    if (same.length == 0) {
      alert("해당 이름은 존재하지 않습니다.");
      return;
    }
    const current = prompt(`이름을 무엇으로 바꾸고 싶은가요?`);
    // setPerson((prev) => ({
    //   ...prev,
    //   mentors: prev.mentors.map((mentor) => {
    //     if (mentor.name !== change) {
    //       return mentor;
    //     } else {
    //       return { ...mentor, name: current };
    //     }
    //   }),
    // }));
    dispatch({ type: "updated", change, current });
  };

이 로직은 이름을 update 하는 로직인데, setPerson을 쓰는 대신 dispatch 함수 한 줄로 간단하게 작성할 수 있다.

액션 객체({type: "updated"})를 전달하고, 그 액션에 필요한 데이터(change, current)를 전달해준다!

나머지 추가, 삭제도 똑같은 방식으로 dispatch를 호출한다!

useReducer 쓰니까 set을 일일이 해주지 않아도 되서 뭔가 편한 것 같기도 하고,, 파일을 따로 만들어주고 그대로 import만 해서 쓰면 되니까 익숙해지면 편할 것 같긴 하다! 알아보니 복잡한(객체 안에 객체 또 객체) 객체가 아니라면 별도의 useReducer 없이 useState만으로 충분히 코딩할 수 있다 하니 복잡한 프로젝트에서 주로 사용되는 hook인 것 같다!

profile
프론트엔드 대장이 되어보쟈

0개의 댓글