CHZZK-Study / Grass-Diary-Client

취지직 2팀 프로젝트 Grass Diary
1 stars 3 forks source link

✨ feat: zustand 적용 #168

Closed rkdcodus closed 2 months ago

rkdcodus commented 2 months ago

✅ 체크리스트

📝 작업 상세 내용

1️⃣ auth store 수정

2️⃣ user store 생성

user state

const useUserStore = create<UserState>(set => ({
  memberId: 0,
  setMemberId: memberId => set({ memberId }),
}));

export const useMemberId = () => useUserStore(state => state.memberId);
export const useSetMemberId = () => useUserStore(state => state.setMemberId);

useUser 훅

export const useUser = (): Id => {
  const { isAuthenticated } = useAuth();
  const memberId = useMemberId();
  const setMemberId = useSetMemberId();

  const { data, isSuccess, isError } = useQuery<
    number,
    Error,
    number,
    string[]
  >({
    queryKey: ['memberId'],
    queryFn: fetchAxios,
    enabled: !!isAuthenticated,
  });

  useEffect(() => {
    if (!isAuthenticated) setMemberId(0);
    else if (isError) setMemberId(0);
    else if (isSuccess) setMemberId(data);
  }, [isAuthenticated, isError, isSuccess]);

  return memberId;
};

3️⃣ profile store 생성

profile state

const useProfileStore = create<ProfileState>(set => ({
  profileImageURL: '',
  nickname: '',
  profileIntro: '',
  actions: {
    setProfileImageURL: profileImageURL => set({ profileImageURL }),
    setNickName: nickname => set({ nickname }),
    setProfileIntro: profileIntro => set({ profileIntro }),
  },
}));

export const useProfileImageURL = () => useProfileStore(state => state.profileImageURL);
export const useNickname = () => useProfileStore(state => state.nickname);
export const useProfileIntro = () => useProfileStore(state => state.profileIntro);
export const useProfileActions = () => useProfileStore(state => state.actions);

useProfile 훅

export const useProfile = () => {
  const memberId = useUser();
  const { setProfileImageURL, setNickName, setProfileIntro } = useProfileActions();
  const profileImageURL = useProfileImageURL();
  const nickname = useNickname();
  const profileIntro = useProfileIntro();

  const { data, isSuccess, isError, error } = useQuery({
    queryKey: ['profile'],
    queryFn: () => fetchAxios(memberId),
    enabled: !!memberId,
  });

  useEffect(() => {
    if (isError) console.error(CONSOLE_ERROR.PROFILE.GET + error);
    if (isSuccess) {
      setProfileImageURL(data.profileImageURL);
      setNickName(data.nickname);
      setProfileIntro(data.profileIntro);
    }
  }, [isError, isSuccess]);

  return { profileImageURL, nickname, profileIntro };
};

테스트

에러없이 잘 돌아가는 걸 확인했습니다!

🚨 버그 발생 이유

작업하면서 발생했던 에러를 정리하자면 아래와 같습니다

Rendered more hooks than during the previous render.

마이페이지에서 새로고침시 위 에러가 발생하면서 렌더링이 되지 않았습니다. 마이페이지의 ProfileSection 컴포넌트에서 발생하는 것을 알게되었고 원인은 useProfile에 있었습니다. 그때 당시의 useProfileStore와 useProfile 코드입니다

const useProfileStore = create<ProfileState>(set => ({
  profileImageURL: '',
  nickname: '',
  profileIntro: '',
  actions: {
    setProfileImageURL: profileImageURL => set({ profileImageURL }),
    setNickName: nickname => set({ nickname }),
    setProfileIntro: profileIntro => set({ profileIntro }),
    fetchProfile: () => {
      const memberId = useUser();

      if (!memberId) return;

      const { data, isError, error } = useQuery({
        queryKey: ['profile'],
        queryFn: () => fetchAxios(memberId),
      });

      if (isError) console.error(CONSOLE_ERROR.PROFILE.GET + error);
      if (data) {
        set({ profileImageURL: data.profileImageURL });
        set({ nickname: data.nickname });
        set({ profileIntro: data.profileIntro });
      }
    },
  },
}));

export const useProfile = () => {
  const profileImageURL = useProfileImageURL();
  const nickname = useNickname();
  const profileIntro = useProfileIntro();
  const { fetchProfile } = useProfileActions();
  fetchProfile();

  return { profileImageURL, nickname, profileIntro };
};

챗지피티에게 물어보니 이 에러는 React의 함수형 컴포넌트에서 Hook 호출 규칙을 위반할 때 발생한다고 합니다.

useProfile 훅에서 fetchProfile 함수를 호출하는데, 이 함수 내에서 useQuery 훅이 사용되고 있고 useQuery는 fetchProfile 함수가 조건에 따라 호출되기 때문에, 컴포넌트의 렌더링 과정에서 조건부로 훅이 실행될 수 있습니다. 이는 훅의 호출 순서가 변경될 수 있다는 것을 의미하며, 이것이 오류의 원인이 됩니다. 라고 했습니다..

그래서 useProfile에서 useQuery를 직접 호출하는 식으로 해결했습니다.

🔎 후속 작업 (선택 사항)

🤔 질문 사항 (선택 사항)

zustand가 처음이라 이 방법이 옳은 방법인지 판단이 잘 서지 않습니다! 도와주세요!

📚 참고 자료 (선택 사항)

Zustand 사용법 zustand 어떻게 써야 잘썼다고 소문날까 Zustand 현명하게 사용하기 (불필요한 리렌더링 막기)

📸 스크린샷 (선택 사항)

변경 사항에 대한 스크린샷이 있다면 첨부해주세요.

✅ 셀프 체크리스트

이슈 번호: #98

rkdcodus commented 2 months ago

넵!! 추후 최적화 진행해보겠습니다!