iT邦幫忙

2024 iThome 鐵人賽

DAY 17
0

本系列文章 GitHub

今天我們將繼續優化 Todo 列表,昨天我們將其內容放在了 App.tsx 中:

import './App.css'
import Header from './components/Header'
import logo from './assets/logo.png'
import { useState } from 'react'
import TodoList from './components/TodoList'

export type TodoItem = {
  id: number
  title: string
  isFinished: boolean
}

function App() {
  const [todos, setTodos] = useState<TodoItem[]>([])

  const createTodoHandler = () => {
    const newTodo: TodoItem = {
      id: Math.random(),
      title: 'Learn JavaScript',
      isFinished: false,
    }
    setTodos((prevTodos) => [...prevTodos, newTodo])
  }

  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>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id} className='list-none'>
            <Todo isFinished={todo.isFinished}>
              <p>{todo.title}</p>
            </Todo>
          </li>
        ))}
      </ul>
    </main>
  )
}

export default App

現在,我們要將這段列出 Todo 的程式碼獨立出來,提取成一個專門的元件,讓程式碼更加結構化:

<ul>
  {todos.map((todo) => (
    <li key={todo.id} className='list-none'>
      <Todo isFinished={todo.isFinished}>
        <p>{todo.title}</p>
      </Todo>
    </li>
  ))}
</ul>

首先,在 components 資料夾建立 TodoList.tsx,並將上方的程式碼貼入:

export default function TodoList() {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id} className='list-none'>
          <Todo isFinished={todo.isFinished}>
            <p>{todo.title}</p>
          </Todo>
        </li>
      ))}
    </ul>
  )
}

接下來,我們要為 TodoListprops 定義型別,以確保 todos 的資料結構一致,從而提高型別安全性:

import Todo from './Todo'

type TodoListProps = {
  todos: {
    id: number
    title: string
    isFinished: boolean
  }[]
}

export default function TodoList({ todos }: TodoListProps) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id} className='list-none'>
          <Todo isFinished={todo.isFinished}>
            <p>{todo.title}</p>
          </Todo>
        </li>
      ))}
    </ul>
  )
}

App.tsx 中引入 TodoList 元件,並傳入 todos

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>
      <TodoList todos={todos} />
    </main>
  )

雖然目前運作正常,但我們會發現 App.tsxTodoList.tsx 中的型別定義是重複的。為了避免重複定義型別,我們可以將 TodoItem 的型別匯出並共享。首先,將 TodoItem 型別從 App.tsx 匯出:

export type TodoItem = {
  id: number
  title: string
  isFinished: boolean
}

接著,到 TodoList.tsx 中匯入這個型別,並替換掉重複的部分:

import Todo from './Todo'
import { type TodoItem } from '../App'

type TodoListProps = {
  todos: TodoItem[]
}

export default function TodoList({ todos }: TodoListProps) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id} className='list-none'>
          <Todo isFinished={todo.isFinished}>
            <p>{todo.title}</p>
          </Todo>
        </li>
      ))}
    </ul>
  )
}

這樣的修改不僅減少了重複代碼,還提高了型別的可重用性和一致性,讓程式碼在維護和擴展時更加便捷。


【 補充 】

在上面的範例中,我們匯出了使用 type 方法定義的型別:

export type TodoItem = {
  id: number
  title: string
  isFinished: boolean
}

這樣的寫法,在 interface 也是完全成立的:

export interface TodoItem {
  id: number
  title: string
  isFinished: boolean
}

匯出型別的好處

在大型專案中,匯出型別能夠有效提升程式碼的維護性。當我們將型別獨立出來並匯出後,不僅可以在多個元件間共享相同的型別定義,還能確保型別的一致性。這樣的做法可以避免重複編寫相同的型別邏輯,減少潛在的錯誤風險。同時,如果日後有需求修改型別結構,只需在一個地方進行調整,所有使用該型別的地方都會自動更新,讓開發過程更加順暢和高效。


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

尚未有邦友留言

立即登入留言