前一篇我們已經完成了一個簡單版的 React TodoList,接著這一篇我們要來針對這個簡單版的 TodoList 優化一下,讓它變得更完整。
雖然我們學會了很基本的 React TodoList 製作,但從頭到尾都只是寫在 App 中,因此接下來要嘗試把某一個區塊拔出來作為一個獨立元件。
而這邊最適合的地方是 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 參數要傳入 todoList
與 updateTodo
這兩個參數
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
中,並且將 todoList
與 updateTodo
這兩個參數傳入 List
中
<List todoList={ todoList } updateTodo={ updateTodo }/>
這時候你在去試著輸入代辦事項之後,就可以發現畫面正常渲染出代辦事項了。
當我們關閉網頁之後,剛剛所寫的代辦事項都會被清空,那麼為了記錄下來,所以就會需要使用到 LocalStorage 的技巧。
而這邊通常來講,你可能會將 localStorage
行為寫在 addTodo
與 updateTodo
中
// ...略
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
中呢?這邊有兩個原因
useState
會在元件第一次渲染時執行,而 useEffect
則是在元件渲染後才會執行,因此這邊我們就直接寫在 useState
中即可。useEffect
去取得 localStorage
的資料,那麼當我們執行 setTodoList(JSON.parse(localStorage.getItem('todoList')) || []);
時,那麼 todoList
就會有變化,這就會再次觸發 useEffect
,這樣就會造成無限迴圈的狀況,因此基於這個狀況並不適合使用 useEffect
現在我們不論是重新整理、新增或是更新資料等行為都會被寫入到 localStorage
中,我們也活用了前面所學的一些技巧哩。
最後也一樣提供完整版 React - TodoList CodePen 的連結。
不免俗也提供 Vue 版本的 Vue - TodoList CodePen 可以讓你比較一下兩者的差異。
本文將會同步更新到我的部落格