之前我們將新增 todo
的 input
以及按鈕放在 App.tsx
,現在我們要將它提出為獨立的元件:
<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>Create</button>
</div>
請在 components
資料夾內創建一個 CreateTodo.tsx
,並且將以上程式碼貼入該元件的返回內容:
export default function CreateTodo() {
return (
<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>Create</button>
</div>
)
}
接著,將原本在 App.tsx
中的重複內容替換成 CreateTodo
元件:
...
return (
<main className='w-[500px] h-[100dvh] portrait:w-[90%] flex flex-col'>
<Header image={{ src: logo, alt: 'logo' }}>
<h1>Todo List</h1>
</Header>
<CreateTodo />
<TodoList todos={todos} onDeleteTodo={deleteTodoHandler} />
</main>
)
...
今天我們會提到表單的型別,因此在這邊需要將 CreateTodo
的父層改為表單:
export default function CreateTodo() {
return (
<form 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>Create</button>
</form>
)
}
當表單中放入 <button>
,且未指定 type
時,該按鈕會預設為 submit
,這將觸發頁面的重新整理。為了防止這一預設行為,我們可以使用 event.preventDefault()
阻止預設的表單提交動作:
export default function CreateTodo() {
const createTodoHandler = (event) => {
event?.preventDefault()
}
return (
<form className='w-full mb-[20px] h-fit flex' onSubmit={createTodoHandler}>
<label htmlFor='newTodo'></label>
<input
type='text'
id='newTodo'
name='newTodo'
className='flex-1 mr-[16px] px-3 rounded-[5px] focus:outline-none'
/>
<button>Create</button>
</form>
)
}
但當你按照上方程式碼寫入時,會發現 IDE 發出了錯誤警告,根據截圖可以看出 event
被判定為 any
型別:
實際上的 event
又該是什麼型別呢?
當處理表單事件時,event
是核心的參數之一,如果不為它明確定義型別,TypeScript 會推斷為 any
,這會導致 IDE 無法提供正確的型別檢查。
我們可以使用 React 提供的 FormEvent
來為 event
指定型別,確保事件處理過程中的型別安全性:
import { type FormEvent } from 'react'
export default function CreateTodo() {
const createTodoHandler = (event: FormEvent) => {
event?.preventDefault()
}
return (
<form className='w-full mb-[20px] h-fit flex' onSubmit={createTodoHandler}>
<label htmlFor='newTodo'></label>
<input
type='text'
id='newTodo'
name='newTodo'
className='flex-1 mr-[16px] px-3 rounded-[5px] focus:outline-none'
/>
<button>Create</button>
</form>
)
}
也可以試著使用 inline
的方式寫入事件,例如:onSubmit={(event)=>{}}
。
當我們這樣寫時,TypeScript 會自動推斷 event
為 FormEvent
,因為它可以根據表單提交事件的上下文做出判斷。而當我們將事件處理函式作為獨立函式時,TypeScript 則無法自動推斷 event
的型別,因此會將其推斷為 any
: