iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 26
1
Modern Web

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

[Day 26] React 攻城戰 - 加入新增看板功能

  • 分享至 

  • xImage
  •  

為了加入新增看板的功能,我們要來建立第二個元件取名叫做 AddBoard。在 src/components 資料夾中新增 AddBoard 資料夾,然後在 AddBoard 資料夾底下新增名稱為 AddBoard.js 的檔案:

import React from "react";

const AddBoard = () => {
  return (
    <div>
      <button>+新增看板</button>
    </div>
  );
};

export default AddBoard;

並且在 App.js 引入來使用:

import React from 'react';

import { reducer, initialState } from './reducer'; 
// 引入 AddBoard 元件
import AddBoard from "../AddBoard/AddBoard";

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  
  const [isEditingTitle, setIsEditingTitle] = React.useState(false);
  
  React.useEffect(() => {
    document.title = state.title;
  }, [state.title]);
  
  return (
    <div>
      {isEditingTitle ? (
        <input
          value={state.title}
          autoFocus
          onKeyPress={e => {
            if (e.key === "Enter") {
              setIsEditingTitle(false);
            }
          }}
          onChange={e => {
            dispatch({
              type: "CHANGE_TITLE",
              payload: {
                title: e.target.value
              }
            });
          }}
        />
      ) : (
        <h1
          onClick={() => setIsEditingTitle(true)}
        >{state.title}</h1>
      )}
      <div>
        {/* 使用 AddBoard 元件 */ }
        <AddBoard />
      </div>
    </div>
  );
};

export default App;

切換新增模式

其中有一個新增看板的按鈕。接下來我們要新增一個 state 來控制是否進入新增模式,若進入新增模式會顯示一個文字輸入框來輸入新看板名稱和一個確定新增看板的按鈕,而在滑鼠點擊別處或是按鈕時就會離開新增模式:

import React from "react";

const AddBoard = () => {
  const [isAddingBoard, setIsAddingBoard] = React.useState(false);

  return (
    <div>
      {isAddingBoard ? (
        <>
          <input autoFocus onBlur={() => setIsAddingBoard(false)} />
          <button onClick={() => setIsAddingBoard(false)}>+新增</button>
        </>
      ) : (
        <button onClick={() => setIsAddingBoard(true)}>+新增看板</button>
      )}
    </div>
  );
};

export default AddBoard;

輸入新看板標題

再來要讓使用者能夠輸入新看板標題,因此我們再新增一個 state 來儲存使用者在文字輸入框輸入的文字:

import React from "react";

const AddBoard = () => {
  const [isAddingBoard, setIsAddingBoard] = React.useState(false);
  const [inputValue, setInputValue] = React.useState('');

  return (
    <div>
      {isAddingBoard ? (
        <>
          <input
            autoFocus
            onBlur={() => setIsAddingBoard(false)}
            // 由 inputValue 來控制文字輸入框內的文字
            value={inputValue}
            // 偵測使用者輸入的事件
            onChange={e => setInputValue(e.target.value)}
          />
          <button onClick={() => setIsAddingBoard(false)}>+新增</button>
        </>
      ) : (
        <button onClick={() => setIsAddingBoard(true)}>+新增看板</button>
      )}
    </div>
  );
};

export default AddBoard;

新增看板功能

接著要在使用者按下確定新增的按鈕時新增看板,我們需要呼叫 dispatch 一個新增看板的 action 物件 (在之前一篇文章中定義過新增看板的 action 物件),dispatch 這個函式要透過 props 從 App 元件傳進來才能取得:

App 元件

<AddBoard dispatch={dispatch} />

AddBoard 元件

import React from "react";

// 取得 props 參數
const AddBoard = props => {
  const [isAddingBoard, setIsAddingBoard] = React.useState(false);
  const [inputValue, setInputValue] = React.useState('');

  return (
    <div>
      {isAddingBoard ? (
        <>
          <input
            autoFocus
            onBlur={() => setIsAddingBoard(false)}
            value={inputValue}
            onChange={e => setInputValue(e.target.value)}
          />
          <button
            onClick={() => setIsAddingBoard(false)}
            // 偵測按鈕被點擊的事件
            onMouseDown={() => {
              props.dispatch({
                type: "ADD_BOARD",
                payload: {
                  boardName: inputValue
                }
              });
              // 並且清空文字輸入框內的文字
              setInputValue("");
            }}
          >+新增</button>
        </>
      ) : (
        <button onClick={() => setIsAddingBoard(true)}>+新增看板</button>
      )}
    </div>
  );
};

export default AddBoard;

加上元件樣式

在 src/components/App 資料夾底下新增 App.module.scss 檔案:

.app {
  min-height: calc(100vh - 96px);
  padding-bottom: 64px;
}

.title {
  color: white;
  padding: 8px;
  
  &:hover {
    text-decoration: underline;
    cursor: pointer; 
  }
}

.titleInput {
  color: black;
}

.boards {
  display: flex;
  align-items: flex-start;
  margin-top: 8px;
}

App 元件引入樣式檔案,並且加入 className(以下僅表示大致元素結構):

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

<div className={styles.app}>
  {isEditingTitle ? (
    <input className={styles.titleInput} />
  ) : (
    <h1 className={styles.title}>
      {state.title}
    </h1>
  )}
  <div className={styles.boards}>
    <AddBoard />
  </div>
</div>

同樣在 src/components/AddBoard 資料夾底下新增 AddBoard.module.scss 檔案:

.addBoard {
  margin-right: 8px;
  border-radius: 4px;
  width: 320px;
  background-color: darkgray;
  padding: 8px;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;

  .addBoardButton {
    color: white;
  }

  .addButton {
    background: green;
    color: white;
    margin-top: 8px;
    padding: 8px;
    border-radius: 4px;
  }
}

AddBoard 元件引入樣式檔案,並且加入 className(以下僅表示大致元素結構):

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

<div className={styles.addBoard}>
  {isAddingBoard ? (
    <>
      <input />
      <button className={styles.addButton}>
        +新增
      </button>
    </>
  ) : (
    <button className={styles.addBoardButton}>
      +新增看板
    </button>
  )}
</div>

成果畫面

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

下集預告

建立看板的元件來顯示看板的資料。


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

尚未有邦友留言

立即登入留言