今天我們將為 Todo List 加入新增 Todo 的功能。暫時先將此功能直接放在 App.tsx 中,後續我們會進行優化,將其獨立成一個元件。請先將 App.tsx 的內容修改如下:
import './App.css'
import Header from './components/Header'
import Todo from './components/Todo'
import logo from './assets/logo.png'
function App() {
const createTodoHandler = () => {
}
return (
<main className='w-[500px] h-[100dvh] portrait:w-[90%] flex flex-col'>
<Header image={{ src: logo, alt: 'logo' }}>
<h1>Todo List</h1>
</Header>
<div className='w-full mb-[20px] h-fit flex'>
<label htmlFor='newTodo'></label>
<input
type='text'
id='newTodo'
name='newTodo'
className='flex-1 mr-[16px] px-3 rounded-[5px] focus:outline-none'
/>
<button onClick={createTodoHandler}>Create</button>
</div>
<Todo isFinished={false}>
<p>Learn typeScript</p>
</Todo>
</main>
)
}
export default App
這裡我們將焦點放在 TypeScript 的應用上,對於 React 語法和樣式部分就不再詳述。
當前畫面:
接下來,我們將使用 useState 來儲存 todos。首先,將狀態初始值定義為一個空字串,試著觀察它的型別推斷:
const [todos, setTodos] = useState('')
將滑鼠移到 todos 上方,可以看到 IDE 推斷其型別為字串:
但 todos 應該是多筆資料的集合,每筆資料包含 id、title 和 isFinished 屬性。因此,我們需要將狀態的初始值設定為一個陣列:
const [todos, setTodos] = useState([])
這邊需要設定 id 是因為我們會使用 map 方法來處理資料,而使用 map 產生列表,需要指定唯一的 key,這是 React 本身的規範,這邊的 id 便是為了 key 而設。
如需了解更多 React key 的相關內容,可以參照官方文件。
當我們宣告這樣的狀態後,將滑鼠移到 todos 上,會發現 IDE 顯示 todos 的型別為 never[]:
這是因為我們只給了空陣列,並未指定陣列內元素的型別。因此,我們需要為 useState 提供一個泛型,告訴它應該存儲什麼型別的資料。定義 TodoItem 型別:
type TodoItem = {
id: number
title: string
isFinished: boolean
}
接著,將 TodoItem 型別放入 useState 的泛型參數中:
const [todos, setTodos] = useState<TodoItem[]>([])
這也可以寫成:
const [todos, setTodos] = useState<Array<TodoItem>>([])
雖然兩種寫法都正確,但基於可讀性,我們會採用第一種方式。
現在,讓我們在 createTodoHandler 中建立一筆假資料,並更新 todos 狀態:
const createTodoHandler = () => {
const newTodo: TodoItem = {
id: Math.random(),
title: 'Learn JavaScript',
isFinished: false,
}
setTodos((prevTodos) => [...prevTodos, newTodo])
}
原先我們渲染 Todo 元件的方式是:
<Todo isFinished={false}>
<p>Learn typeScript</p>
</Todo>
現在,我們需要用 map 方法來渲染多筆 Todo 資料:
<ul>
{todos.map((todo) => (
<li key={todo.id} className='list-none'>
<Todo isFinished={todo.isFinished}>
<p>{todo.title}</p>
</Todo>
</li>
))}
</ul>
重新整理頁面後,你會看到一個空的 Todo List:

點擊 Create 按鈕後,剛才建立的假資料會顯示在列表中:
