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

前言

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

React 元件化

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

List 元件製作

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

1
2
3
4
5
6
7
8
9
10
11
12
<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 這兩個參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

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

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

儲存代辦資料到 LocalStorage

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ...略

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

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

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

1
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

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

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

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

後記

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