iT邦幫忙

2024 iThome 鐵人賽

DAY 19
1

本系列文章 GitHub

之前我們將新增 todoinput 以及按鈕放在 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 型別:

https://ithelp.ithome.com.tw/upload/images/20240929/20169025A2hck5AzzU.png

實際上的 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 會自動推斷 eventFormEvent,因為它可以根據表單提交事件的上下文做出判斷。而當我們將事件處理函式作為獨立函式時,TypeScript 則無法自動推斷 event 的型別,因此會將其推斷為 any

https://ithelp.ithome.com.tw/upload/images/20240929/20169025Ghsfr1xbof.png


上一篇
【 Day 18 】透過 props 實作刪除功能
下一篇
【 Day 20 】useRef with TypeScript
系列文
React 開發者的 TypeScript 探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言