iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 29
1
Modern Web

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

[Day 29] React 攻城戰 - 加入新增卡片功能

  • 分享至 

  • xImage
  •  

新增卡片功能

在原先的 Board 元件中,加入一個按鈕,在被點擊時,改成顯示一個文字輸入框,讓使用者輸入新卡片的內容:

import React from "react";

import styles from "./Board.module.scss";

const Board = props => {
  const [isEditingName, setIsEditingName] = React.useState(false);
  
  // 加入此 state 來控制是否正在新增卡片
  const [isAddingCard, setIsAddingCard] = React.useState(false);

  return (
    <div className={styles.board}>
      <button
        className={styles.closeButton}
        onClick={() => {
          props.dispatch({
            type: "REMOVE_BOARD",
            payload: {
              boardId: props.id
            }
          });
        }}
      >
        x
      </button>
      {isEditingName ? (
        <input
          autoFocus
          value={props.name}
          className={styles.editNameInput}
          onChange={e => {
            dispatch({
              type: "CHANGE_BOARD_NAME",
              payload: {
                boardId: props.id,
                boardName: e.target.value
              }
            });
          }}
          onKeyPress={e => {
            if (e.key === "Enter") {
              setIsEditingName(false);
            }
          }}
        />
      ) : (
        <h2
          className={styles.name}
          onClick={() => setIsEditingName(true)}
        >
          {props.name}
        </h2>
      )}
      {/* 新增以下程式碼 */}
      {isAddingCard ? (
        <input autoFocus />
      ) : (
        <button
          // 偵測按鈕被點擊的事件
          onClick={() => setIsAddingCard(true)}
        >
          +新增卡片
        </button>
      )}
    </div>
  );
};

接著新增一個 state 來控制文字輸入框中的內容:

import React from "react";

import styles from "./Board.module.scss";

const Board = props => {
  const [isEditingName, setIsEditingName] = React.useState(false);
  const [isAddingCard, setIsAddingCard] = React.useState(false);
  
  // 加入此 state 來控制文字輸入框中的內容
  const [inputValue, setInputValue] = React.useState("");

  return (
    <div className={styles.board}>
      <button
        className={styles.closeButton}
        onClick={() => {
          props.dispatch({
            type: "REMOVE_BOARD",
            payload: {
              boardId: props.id
            }
          });
        }}
      >
        x
      </button>
      {isEditingName ? (
        <input
          autoFocus
          value={props.name}
          className={styles.editNameInput}
          onChange={e => {
            dispatch({
              type: "CHANGE_BOARD_NAME",
              payload: {
                boardId: props.id,
                boardName: e.target.value
              }
            });
          }}
          onKeyPress={e => {
            if (e.key === "Enter") {
              setIsEditingName(false);
            }
          }}
        />
      ) : (
        <h2
          className={styles.name}
          onClick={() => setIsEditingName(true)}
        >
          {props.name}
        </h2>
      )}
      {isAddingCard ? (
        <input
          autoFocus
          // 傳入 inputValue 的 state
          value={inputValue}
          // 偵測使用者輸入文字的事件
          onChange={e => setInputValue(e.target.value)}
        />
      ) : (
        <button
          onClick={() => setIsAddingCard(true)}
        >
          +新增卡片
        </button>
      )}
    </div>
  );
};

再來希望使用者按下 Enter 時新增卡片:

import React from "react";

import styles from "./Board.module.scss";

const Board = props => {
  const [isEditingName, setIsEditingName] = React.useState(false);
  const [isAddingCard, setIsAddingCard] = React.useState(false);
  const [inputValue, setInputValue] = React.useState("");

  return (
    <div className={styles.board}>
      <button
        className={styles.closeButton}
        onClick={() => {
          props.dispatch({
            type: "REMOVE_BOARD",
            payload: {
              boardId: props.id
            }
          });
        }}
      >
        x
      </button>
      {isEditingName ? (
        <input
          autoFocus
          value={props.name}
          className={styles.editNameInput}
          onChange={e => {
            dispatch({
              type: "CHANGE_BOARD_NAME",
              payload: {
                boardId: props.id,
                boardName: e.target.value
              }
            });
          }}
          onKeyPress={e => {
            if (e.key === "Enter") {
              setIsEditingName(false);
            }
          }}
        />
      ) : (
        <h2
          className={styles.name}
          onClick={() => setIsEditingName(true)}
        >
          {props.name}
        </h2>
      )}
      {isAddingCard ? (
        <input
          autoFocus
          value={inputValue}
          onChange={e => setInputValue(e.target.value)}
          // 偵測按下 Enter 的事件
          onKeyPress={e => {
            if (e.key === "Enter") {
              dispatch({
                type: "ADD_CARD",
                payload: {
                  boardId: props.id,
                  cardValue: inputValue.trim()
                }
              });
              // 同時清除文字框中的內容
              setInputValue("");
              // 同時離開新增卡片模式
              setIsAddingCard(false);
            }
          }}
        />
      ) : (
        <button
          onClick={() => setIsAddingCard(true)}
        >
          +新增卡片
        </button>
      )}
    </div>
  );
};

最後在 Board.module.scss 加入樣式:

.board {
  .addCardInput {
    padding: 8px;
    margin: 4px 0;
  }

  .addCardButton {
    color: white;
    padding: 8px;
    margin: 4px 0;

    &:hover {
      text-decoration: underline;
    }
  }
}

Board 元件中加入 className(以下僅表示大致元素結構):

<div className={styles.board}>
  <button className={styles.closeButton}>
    x
  </button>
  {isEditingName ? (
    <input className={styles.editNameInput} />
  ) : (
    <h2 className={styles.name}>
      {props.name}
    </h2>
  )}
  {isAddingCard ? (
    <input className={styles.addCardInput} />
  ) : (
    <button className={styles.addCardButton}>
      +新增卡片
    </button>
  )}
</div>

成果畫面

到目前為止,我們可以輸入新卡片內容,不過按下新增按鈕還看不到新增出來的卡片,是因為新卡片的資料雖然被新增到 useReducer 的 state 中,但我們並沒有將 state 中的資料顯示出來。

下集預告

建立卡片的元件來顯示卡片的資料並且加入編輯卡片內容的功能。


上一篇
[Day 28] React 攻城戰 - 加入刪除看板與編輯看板名稱功能
下一篇
[Day 30] React 攻城戰 - 顯示卡片
系列文
使用 React 製作簡易專案管理網站:從基礎到實戰30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言