iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
1
Modern Web

使用 React 製作簡易專案管理網站:從基礎到實戰系列 第 23

[Day 23] React 攻城戰 - 資料更新邏輯 reducer

  • 分享至 

  • xImage
  •  

上一篇文章我們定義好各種更新資料的 action 物件,這些物件會在呼叫 dispatch 函式傳入 reducer 函式,並且根據 type 屬性以及目前的 state 來決定如何更新資料,所以一個 reducer 大概會長這樣:

function reducer(state, action) {
  switch (action.type) {
    case 'ACTION_1':
      // 更新邏輯
      // return newState;
    case 'ACTION_2':
      // 更新邏輯
      // return newState;
    default: 
      return state;
  }
}

依照上一篇文章定義好的各種 action type 都有一個對應的處理方式。

reducer

function reducer() {
  switch (action.type) {
    // 編輯標題
    case "CHANGE_TITLE": {
      const { title } = action.payload;
      return {
        ...state,
        title
      };
    }
    
    // 新增看板
    case "ADD_BOARD": {
      const boardId = shortid.generate();
      const { boardName } = action.payload;
      return {
        ...state,
        boards: {
          ids: [...state.boards.ids, boardId],
          byId: {
            ...state.boards.byId,
            [boardId]: {
              name: boardName,
              cardIds: []
            }
          }
        }
      };
    }
    
    // 修改看板名稱
    case "CHANGE_BOARD_NAME": {
      const { boardId, boardName } = action.payload;
      return {
        ...state,
        boards: {
          ...state.boards,
          byId: {
            ...state.boards.byId,
            [boardId]: {
              ...state.boards.byId[boardId],
              name: boardName
            }
          }
        }
      };
    }
    
    // 移動看板
    case "MOVE_BOARD": {
      const { draggingBoardId, targetBoardIndex } = action.payload;
      const newBoardsIds = [...state.boards.ids];
      const sourceBoardIndex = state.boards.ids.findIndex(
        boardId => boardId === draggingBoardId
      );
      newBoardsIds.splice(sourceBoardIndex, 1);
      newBoardsIds.splice(targetBoardIndex, 0, draggingBoardId);
      return {
        ...state,
        boards: {
          ...state.boards,
          ids: newBoardsIds
        }
      };
    }
    
    // 刪除看板
    case "REMOVE_BOARD": {
      const { boardId } = action.payload;
      const newBoardsIds = state.boards.ids.filter(id => id !== boardId);
      const newBoardsById = { ...state.boards.byId };
      delete newBoardsById[boardId];

      const newCardsById = { ...state.cards.byId };
      state.boards.byId[boardId].cardIds.forEach(cardId => {
        delete newCardsById[cardId];
      });

      return {
        ...state,
        boards: {
          ids: newBoardsIds,
          byId: newBoardsById
        },
        cards: {
          byId: newCardsById
        }
      };
    }

    // 新增卡片
    case "ADD_CARD": {
      const cardId = shortid.generate();
      const { boardId, cardValue } = action.payload;
      return {
        ...state,
        boards: {
          ...state.boards,
          byId: {
            ...state.boards.byId,
            [boardId]: {
              ...state.boards.byId[boardId],
              cardIds: [...state.boards.byId[boardId].cardIds, cardId]
            }
          }
        },
        cards: {
          byId: {
            ...state.cards.byId,
            [cardId]: cardValue
          }
        }
      };
    }
    
    // 修改卡片內容
    case "CHANGE_CARD_VALUE": {
      const { cardId, cardValue } = action.payload;
      return {
        ...state,
        cards: {
          byId: {
            ...state.cards.byId,
            [cardId]: cardValue
          }
        }
      };
    }
    
    // 移動卡片
    case "MOVE_CARD": {
      const { draggingCardId, targetBoardId, targetCardIndex } = action.payload;
      const sourceBoardId = Object.keys(state.boards.byId).find(boardId => {
        return state.boards.byId[boardId].cardIds.find(
          cardId => cardId === draggingCardId
        );
      });
      const sourceBoardCardIds = [...state.boards.byId[sourceBoardId].cardIds];
      const targetBoardCardIds =
        sourceBoardId === targetBoardId
          ? sourceBoardCardIds
          : [...state.boards.byId[targetBoardId].cardIds];
      const sourceCardIndex = sourceBoardCardIds.findIndex(
        cardId => cardId === draggingCardId
      );
      sourceBoardCardIds.splice(sourceCardIndex, 1);
      targetBoardCardIds.splice(targetCardIndex, 0, draggingCardId);
      return {
        ...state,
        boards: {
          ...state.boards,
          byId: {
            ...state.boards.byId,
            [sourceBoardId]: {
              ...state.boards.byId[sourceBoardId],
              cardIds: sourceBoardCardIds
            },
            [targetBoardId]: {
              ...state.boards.byId[targetBoardId],
              cardIds: targetBoardCardIds
            }
          }
        }
      };
    }
    
    // 刪除卡片
    case "REMOVE_CARD": {
      const { draggingCardId } = action.payload;
      const sourceBoardId = Object.keys(state.boards.byId).find(boardId => {
        return state.boards.byId[boardId].cardIds.find(
          cardId => cardId === draggingCardId
        );
      });
      const sourceBoardCardIds = [...state.boards.byId[sourceBoardId].cardIds];
      const sourceCardIndex = sourceBoardCardIds.findIndex(
        cardId => cardId === draggingCardId
      );
      sourceBoardCardIds.splice(sourceCardIndex, 1);

      const newCardsById = { ...state.cards.byId };
      delete newCardsById[draggingCardId];
      return {
        ...state,
        boards: {
          ...state.boards,
          byId: {
            ...state.boards.byId,
            [sourceBoardId]: {
              ...state.boards.byId[sourceBoardId],
              cardIds: sourceBoardCardIds
            }
          }
        },
        cards: {
          byId: newCardsById
        }
      };
    }
    
    default:
      return state;
  }
}

如果完全沒有對應的 action type,那就返回原本的 state,另外在新增看板或是新增卡片時,我們使用 shortid 套件來幫看板或卡片建立不重複的 id,方便用來查找看板或卡片。

下集預告

開始建立元件,從根元件開始!


上一篇
[Day 22] React 攻城戰 - 資料更新邏輯 action
下一篇
[Day 24] React 攻城戰 - 根元件和全局樣式
系列文
使用 React 製作簡易專案管理網站:從基礎到實戰30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言