iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Modern Web

終究都要學 React 何不現在學呢?系列 第 11

終究都要學 React 何不現在學呢? - React 基礎 - TodoList (下) - (11)

  • 分享至 

  • xImage
  •  

前言

前一篇我們已經完成了一個簡單版的 React TodoList,接著這一篇我們要來針對這個簡單版的 TodoList 優化一下,讓它變得更完整。

React 元件化

雖然我們學會了很基本的 React TodoList 製作,但從頭到尾都只是寫在 App 中,因此接下來要嘗試把某一個區塊拔出來作為一個獨立元件。

List 元件製作

而這邊最適合的地方是 ul li 的區塊,我們會將這一個區塊拔出來作為一個獨立的元件,然後透過 Props 傳入來渲染畫面

<ul>
  {
    todoList.map((todo) => (
      <li className="py-4" key={ todo.id } data-id={ todo.id } >
        <label className={ todo.status ? 'line-through' : ''}>
          <input onChange={ updateTodo } type="checkbox" className="mr-2"  data-id={ todo.id } checked={ todo.status }/>
          { todo.name }
        </label>
      </li>
    ))
  }
</ul>

第一個動作就是建立一個元件叫做 List ,並把 ui li 的程式碼整合到 List 中,別忘了 Props 參數要傳入 todoListupdateTodo 這兩個參數

const List = ({ todoList, updateTodo }) => {
  return (
    <ul>
      {
        todoList.map((todo) => (
          <li className="py-4" key={ todo.id } data-id={ todo.id } >
            <label className={ todo.status ? 'line-through' : ''}>
              <input onChange={ updateTodo } type="checkbox" className="mr-2"  data-id={ todo.id } checked={ todo.status }/>
              { todo.name }
            </label>
          </li>
        ))
      }
    </ul>
  )
}

接下來將 List 元件嵌入到 App 中,並且將 todoListupdateTodo 這兩個參數傳入 List

<List  todoList={ todoList } updateTodo={ updateTodo }/>

CodePen 連結

這時候你在去試著輸入代辦事項之後,就可以發現畫面正常渲染出代辦事項了。

儲存代辦資料到 LocalStorage

當我們關閉網頁之後,剛剛所寫的代辦事項都會被清空,那麼為了記錄下來,所以就會需要使用到 LocalStorage 的技巧。

而這邊通常來講,你可能會將 localStorage 行為寫在 addTodoupdateTodo

// ...略

const addTodo = (event) => {
  const input = document.querySelector('#todoInput');
  setTodoList([
    ...todoList,
    {
      id: Date.now(),
      name: input.value,
      status: false,
    }
  ]);
  input.value = '';
  window.localStorage.setItem('todoList', JSON.stringify(todoList));
};

const updateTodo = (event) => {
  const { id } = event.target.dataset;
  console.log(id)

  const newTodoList = todoList.map((todo) => {
    if(todo.id === Number(id)) {
      todo.status = !todo.status;
    }
    return todo;
  });

  setTodoList([ ...newTodoList ]);
  window.localStorage.setItem('todoList', JSON.stringify(todoList));
}

// ...略

但是這時候你應該會發現資料好像要到第二筆或第三筆才會寫入到 localStorage 中,這個主要原因是 useState 是一個非同步行為。

因此正確來講我們要將 localStorage 拉出來獨立,並用 useEffect 監聽 todoList 的變化,當它有變化時我們才去寫入資料到 todoList

React.useEffect(() => {
  window.localStorage.setItem('todoList', JSON.stringify(todoList));
}, [todoList]);

最後,當我們每一次重新開啟網頁時,都會期望自動取得儲存在 LocalStorage 的資料,因此接下來要將原有的 useState 稍作調整一下

const [ todoList, setTodoList ] = React.useState(JSON.parse(localStorage.getItem('todoList')) || []);

這時候你可能會想說為什麼不寫 useEffect 去取得 localStorage 的資料,而是直接寫在 useState 中呢?這邊有兩個原因

  1. useState 會在元件第一次渲染時執行,而 useEffect 則是在元件渲染後才會執行,因此這邊我們就直接寫在 useState 中即可。
  2. 如果使用 useEffect 去取得 localStorage 的資料,那麼當我們執行 setTodoList(JSON.parse(localStorage.getItem('todoList')) || []); 時,那麼 todoList 就會有變化,這就會再次觸發 useEffect,這樣就會造成無限迴圈的狀況,因此基於這個狀況並不適合使用 useEffect

CodePen 連結

現在我們不論是重新整理、新增或是更新資料等行為都會被寫入到 localStorage 中,我們也活用了前面所學的一些技巧哩。

最後也一樣提供完整版 React - TodoList CodePen 的連結。

不免俗也提供 Vue 版本的 Vue - TodoList CodePen 可以讓你比較一下兩者的差異。

後記

本文將會同步更新到我的部落格


上一篇
終究都要學 React 何不現在學呢? - React 基礎 - TodoList (上) - (10)
下一篇
終究都要學 React 何不現在學呢? - React 進階 - 深入 JSX - (12)
系列文
終究都要學 React 何不現在學呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言