MinwooPark93 / hanghae99-react-week4-assignment

0 stars 0 forks source link

Redux action creator와 firestore FB 함수의 충돌문제?? #1

Open MinwooPark93 opened 2 years ago

MinwooPark93 commented 2 years ago

액션크리에이터 함수 중 CREATE 를 사용했을때, 파이어베이스 함수와 충돌이 있는지 로직을 잘못짠건지 새로고침을 하기 전에는 포스팅 시 같은 카드 2개가 렌더링 되는 이슈발생 일단 create reducer 를 주석 처리하여 파이어베이스 데이터를 LOAD 해와 렌더링이 되는것을 확인해서 이슈를 해결 했으나 이 부분에 대한 원인과 정확한 해결 방법이 궁금합니다.

// cards.js
import { db } from "../../firebase";
import { collection, doc, getDoc, getDocs, addDoc, updateDoc, deleteDoc } from "firebase/firestore";
import { async } from "@firebase/util";

// let id = 0;

// 액션
const LOAD = "cards/LOAD";
const CREATE = "cards/CREATE";
const UPDATE = "cards/UPDATE";
const DELETE = "cards/DELETE";
// const UPDATE = "cards/UPDATE";
// const REMOVE = "cards/REMOVE";

// 액션 크리에이터
export const loadCards = (payload) => ({
  type: LOAD,
  payload,
});

export const createCards = (payload) => ({
  type: CREATE,
  payload: { ...payload },
});

export const updateCards = (payload) => ({
  type: UPDATE,
  payload,
});

export const deleteCards = (payload) => ({
  type: DELETE,
  payload,
});

// export function updateWidget(widget) {
//   return { type: UPDATE, widget };
// }

// export function removeWidget(widget) {
//   return { type: REMOVE, widget };
// }

// 이니셜 스테이트 초기값
const initialState = {
  cards: [
    // { title: "react", pronun: "react is so funny", subtitle: "react subtitle", desc: "react is so funny", done: false },
    // { title: "node", pronun: "node is good", subtitle: "node subtitle", desc: "react is so funny", done: false },
    // { title: "vue", pronun: "vue is nice", subtitle: "vue subtitle", desc: "react is so funny", done: false },
  ],
};

// 미들웨어
export const loadCardsFB = () => {
  return async function (dispatch) {
    const cards_data = await getDocs(collection(db, "voca"));
    // console.log(cards_data); /////////////////////////////////////////////////////////////////*****

    let cards_arr = [];

    cards_data.forEach((c) => {
      // console.log(c.id);
      // console.log(c.data());
      cards_arr.push({ id: c.id, ...c.data() });
    });
    // console.log(cards_arr);
    cards_arr.sort((a, b) => b.num - a.num);
    dispatch(loadCards(cards_arr));
  };
};

export const createCardsFB = (payload) => {
  return async function (dispatch) {
    const docRef = await addDoc(collection(db, "voca"), payload);
    // const _card = await getDoc(docRef);
    console.log(payload);

    const card = { id: docRef.id, ...payload };

    // console.log((await getDoc(docRef)).data());

    // console.log(card);

    dispatch(createCards(card));
  };
};

export const updateCardsFB = (payload) => {
  return async function (dispatch, getState) {
    // console.log(payload);
    const docRef = await doc(db, "voca", payload.id);
    updateDoc(docRef, { done: !payload.done });

    // dispatch(updateCards(payload));
    // console.log(getState().cards)
    const _card_list = getState().cards.cards;
    const card_index = _card_list.findIndex((c) => {
      return c.id === payload.id;
    });
    dispatch(updateCards(card_index));
    // console.log(card_index)
  };
};

export const deleteCardsFB = (payload) => {
  return async function (dispatch, getState) {
    if (!payload.id) {
      window.alert("아이디가 없습니다.");
      return;
    }
    const docRef = doc(db, "voca", payload.id);
    await deleteDoc(docRef);

    const _card_list = getState().cards.cards;
    const card_index = _card_list.findIndex((c) => {
      return c.id === payload.id;
    });

    dispatch(deleteCards(card_index));
    console.log(card_index);
  };
};

// 리듀서
export default function reducer(state = initialState, action) {
  switch (action.type) {
    case LOAD:
      return { cards: action.payload };

    // case CREATE:
    //   // id++;
    //   return {
    //     cards: [...state.cards, action.payload],
    //   };

    case UPDATE: {
      // console.log("이제 완료할거야!");
      // console.log(state, action);
      const new_cards_list = state.cards.map((cur, idx) => {
        return parseInt(action.payload) === idx ? (cur.done !== true ? { ...cur, done: true } : { ...cur, done: false }) : cur;
        // if (parseInt(action.payload) === idx) {
        //   return { ...cur, done: true };
        // } else {
        //   return cur;
        // }
      });
      // console.log({ cards: new_cards_list });
      return { cards: new_cards_list };
    }

    case DELETE:
      console.log("스테이트 카즈" + state.cards);
      console.log("액션 페이로드" + action.payload);
      return {
        cards: state.cards.filter((cur, idx) => idx !== action.payload),
      };

    default:
      return state;
  }
}
nemyung commented 2 years ago

안녕하세요 민우님, 금주 코멘트를 맡은 오세명입니다. 이번 한 주동안 과제를 성공적으로 수행하신 것에 대해 박수를 보내고 싶습니다! 고생 많으셨어요!

답변을 드리기에 앞서 저와 같은 코멘터는 문제에 대한 해결 방법을 직접적으로 제시하지는 않습니다. 문제를 해결할 수 있는 방법은 여러가지일 것이고 그 해결력은 누가 알려준다고 길러지는게 아니라 직접 부딪히면서 늘려나갈 수 있는 것이기 때문입니다. 이점 양해 부탁드립니다.

  1. 주석처리하신 로직은 잘 짜셨습니다. 다만 리듀서 부분에서 LOAD과 CREATE가 같은 순서로 데이터를 내려주고 있는지를 체크해보시면 좋을 것 같습니다.

  2. 문제가 생긴 로직은 Detail.jsposting 이벤트 핸들러가 동기적으로 실행되는 것에 원인이 있습니다.

    • dispatch(createCardsFB(postDictionary))를 한다고 해서 thunk 로직이 바로 실행되는 것은 아닙니다. 코드가 실행되는 순서를 다시 잘 살펴보시는 것을 권장드립니다.
    • 현재는 navigate(-1)이 먼저 실행되어 loadCardsFB가 실행되고, 이후에 createCardFB가 실행됩니다.
  3. 따라서 문제를 해결하시기 위하여 (1) 코드의 실행 순서를 잘 살펴보시고, (2) 페이지가 다시 라우팅 될 때 hook이 어떻게 동작하는지 파악하신다음, 민우님께서 의도하신 코드 실행 순서대로 posting 이벤트 핸들러를 리팩토링을 해보시면 될 것 같네요.

저의 코멘트는 여기까지입니다. 감사합니다!