今天我們將為 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 按鈕後,剛才建立的假資料會顯示在列表中: