前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过三个实例掌握如何使用 TypeScript 泛型创建可重用的 React 组件

通过三个实例掌握如何使用 TypeScript 泛型创建可重用的 React 组件

作者头像
前端达人
发布2024-06-26 09:54:56
1000
发布2024-06-26 09:54:56
举报
文章被收录于专栏:前端达人前端达人
在前端开发中,React 组件是我们日常开发的基础,而使用 TypeScript 泛型能让这些组件更加灵活和可重用。今天我们就来聊聊如何利用 TypeScript 泛型创建可重用的 React 组件。

在深入具体操作之前,先简单介绍一下泛型的概念。泛型允许你在定义组件时不指定具体的数据类型,而是在使用组件时再指定具体的类型。这样一来,我们的组件就能够适应多种数据类型,不必为每种数据类型分别创建不同的组件。

市面上已经有很多关于 TypeScript 泛型的文章和教程,所以本文将聚焦于如何在 React 组件中使用泛型,让你的组件变得更加灵活和可重用。

接下来,我们将通过实例代码一步步展示如何实现这一目标,让你能够轻松掌握这项技能,并应用到实际项目中去。无论你是刚入门的新手,还是有一定经验的开发者,相信都能从中受益。准备好了吗?让我们开始吧!

一、利用 TypeScript 泛型创建简单的可重用 React 组件

创建一个简单的泛型 React 组件

首先,我们来创建一个泛型 React 组件,它可以接受任何类型的数据并通过一个渲染函数将数据展示出来。

代码语言:javascript
复制
// 定义一个泛型类型的 props
type Props<T> = {
  data: T
  render: (data: T) => React.ReactNode
}

// 创建一个泛型 React 组件
export function GenericComponent<T>({ data, render }: Props<T>) {
  return <div>{render(data)}</div>
}

在这个例子中,GenericComponent 接受一个 render 属性,这个属性是一个函数,它接收类型为 T 的数据并返回一个 React.ReactNode。这种模式通常被称为“render props”,它可以让你更灵活地控制数据的渲染方式。

使用泛型组件渲染字符串

接下来,我们用一个字符串类型的数据来使用这个泛型组件。

代码语言:javascript
复制
import GenericComponent from './GenericComponent'

function RenderString() {
  return (
    <GenericComponent<string> 
      data="Hello, world!" 
      render={(data) => <span>{data.toUpperCase()}</span>} 
    />
  )
}

在这个例子中,我们明确地告诉 GenericComponent 预期接收一个字符串类型的数据。渲染函数将字符串转换为大写,并且 TypeScript 确保了在 render 属性中进行的操作是对字符串类型数据有效的。

使用自定义类型的数据

现在我们用一个自定义类型的数据来使用泛型组件。

代码语言:javascript
复制
type Person = {
  name: string
  age: number
}

import GenericComponent from './GenericComponent'

function RenderPerson() {
  return (
    <GenericComponent<Person>
      data={{ name: 'Alice', age: 30 }}
      render={(data) => (
        <span>
          {data.name} is {data.age} years old
        </span>
      )}
    />
  )
}

在这个例子中,TypeScript 确保 data 属性的数据类型与 render 函数中预期的类型匹配。

使用泛型组件渲染任务列表

最后,我们来看看如何用泛型组件渲染一个任务列表。

代码语言:javascript
复制
import { useState } from 'react'
import GenericComponent from './GenericComponent'

interface TodoItem {
  id: number
  title: string
  completed: boolean
}

export function TodoList() {
  // 初始任务列表
  const initialTodos: TodoItem[] = [
    { id: 1, title: 'Learn React', completed: false },
    { id: 2, title: 'Write blog post', completed: true },
    { id: 3, title: 'Study TypeScript', completed: false },
  ]

  // 用于追踪任务列表的状态
  const [todos, setTodos] = useState<TodoItem[]>(initialTodos)

  // 切换任务完成状态的函数
  function toggleTodoCompletion(id: number) {
    const updatedTodos = todos.map((todo) => 
      (todo.id === id ? { ...todo, completed: !todo.completed } : todo))
    setTodos(updatedTodos)
  }

  // 任务列表的复杂渲染函数
  function renderTodos(todos: TodoItem[]) {
    return (
      <ul>
        {todos.map((todo) => (
          <li
            key={todo.id}
            style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer' }}
            onClick={() => toggleTodoCompletion(todo.id)}
          >
            {todo.title}
          </li>
        ))}
      </ul>
    )
  }

  return <GenericComponent<TodoItem[]>  
    data={todos} 
    render={renderTodos} 
  />
}

在这个例子中,我们创建了一个 TodoList 组件,它使用 GenericComponent 渲染一个任务列表。渲染函数更加复杂,因为它需要处理一个项目列表。TypeScript 确保 data 属性的数据类型与 render 函数中预期的类型匹配。

二、使用泛型在 React 组件中展示数据

在实际开发中,很多时候我们需要从 API 获取数据并展示在页面上。利用 TypeScript 泛型,我们可以创建一个通用的 React 组件来处理这种情况。这样不仅能提高代码的可重用性,还能使组件更加灵活。今天我们就通过一个例子来展示如何实现这一目标。

创建一个用于获取数据的泛型 React 组件

首先,我们创建一个泛型组件 FetchAndDisplay,它可以从指定的 URL 获取数据,并通过一个渲染函数将数据展示出来。

代码语言:javascript
复制
import { useEffect, useState } from 'react'

// 定义一个泛型类型的 props
type Props<T> = {
  url: string
  render: (data: T) => React.ReactNode
}

// 创建一个泛型 React 组件
export function FetchAndDisplay<T>({ url, render }: Props<T>) {
  const [data, setData] = useState<T | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<Error | null>(null)

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url)
        if (!response.ok) {
          throw new Error('Failed to fetch data')
        }
        const json = await response.json()
        setData(json)
      } catch (error) {
        setError(error)
      } finally {
        setLoading(false)
      }
    }
    fetchData()
  }, [url])

  if (loading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>Error: {error.message}</div>
  }

  if (data) {
    return <div>{render(data)}</div>
  }

  return null
}

在这个例子中,FetchAndDisplay 是一个泛型组件,它接受一个 URL 和一个渲染函数。组件使用 fetch 方法从指定的 URL 抓取数据,并在抓取成功后调用渲染函数来展示数据。同时,组件还处理了加载和错误状态。

使用 FetchAndDisplay 组件获取和展示帖子

接下来,我们使用 FetchAndDisplay 组件来取取并展示一组帖子数据。

代码语言:javascript
复制
import { FetchAndDisplay } from './FetchAndDisplay'

type Post = {
  userId: number
  id: number
  title: string
  body: string
}

function RenderPosts(posts: Post[]) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </li>
      ))}
    </ul>
  )
}

export function PostList() {
  return <FetchAndDisplay<Post[]> 
    url='https://jsonplaceholder.typicode.com/posts'  
    render={RenderPosts} 
  />
}

在这个例子中,我们使用 FetchAndDisplay 组件从 JSONPlaceholder API 获取一组帖子数据,并通过 RenderPosts 函数将其展示出来。

使用 FetchAndDisplay 组件获取和展示用户

同样地,我们可以使用同一个 FetchAndDisplay 组件来抓取和展示用户数据。

代码语言:javascript
复制
import { FetchAndDisplay } from './FetchAndDisplay'

type User = {
  id: number
  name: string
  email: string
}

function RenderUsers(users: User[]) {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </li>
      ))}
    </ul>
  )
}

export function UserList() {
  return <FetchAndDisplay<User[]> 
    url='https://jsonplaceholder.typicode.com/users' 
    render={RenderUsers} 
  />
}

在这个例子中,我们使用 FetchAndDisplay 组件从 JSONPlaceholder API 获取一组用户数据,并通过 RenderUsers 函数将其展示出来。这展示了泛型在 React 组件中的强大作用,我们可以用同一个组件处理不同类型的数据获取和展示。

三、使用泛型创建通用的 React 表单组件

在实际开发中,表单是我们常用的组件之一。为了提升代码的复用性和灵活性,我们可以使用 TypeScript 泛型创建一个通用的表单组件。尽管在实际项目中我们通常会使用像 Formik 或 react-hook-form 这样的库来处理表单,但为了演示泛型的强大之处,我们将从头开始创建一个简单的表单组件。

定义表单字段和组件的类型

首先,我们定义一些 TypeScript 类型,用来指定表单字段的结构以及我们的通用表单组件将接受的 props。这些类型确保了类型安全,并帮助我们管理表单的状态和行为。

代码语言:javascript
复制
type FormField = {
  name: string
  label: string
  type: 'text' | 'email' | 'password' // 可以根据需要扩展
}

type GenericFormProps<T> = {
  fields: FormField[]
  initialValues: T
  onSubmit: (values: T) => void
}

创建通用表单组件

接下来,我们创建一个函数组件,它接受字段、初始值和一个提交处理函数作为参数。

代码语言:javascript
复制
import { useState } from 'react'

export function GenericForm<T>({ fields, initialValues, onSubmit }: GenericFormProps<T>) {
  const [values, setValues] = useState<T>(initialValues)

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const { name, value } = e.target
    setValues((prevValues) => ({ ...prevValues, [name]: value }))
  }

  function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault()
    onSubmit(values)
  }

  return (
    <form onSubmit={handleSubmit}>
      {fields.map((field) => (
        <div key={field.name}>
          <label htmlFor={field.name}>{field.label}</label>
          <input type={field.type} name={field.name} 
                 value={(values as any)[field.name]} 
                 onChange={handleChange} />
        </div>
      ))}
      <button type='submit'>Submit</button>
    </form>
  )
}

使用通用表单组件

最后,我们使用通用表单组件,并指定具体的表单字段和初始值。

代码语言:javascript
复制
type UserFormValues = {
  name: string
  email: string
  password: string
}

const userFormFields: FormField[] = [
  { name: 'name', label: 'Name', type: 'text' },
  { name: 'email', label: 'Email', type: 'email' },
  { name: 'password', label: 'Password', type: 'password' },
]

const initialValues: UserFormValues = {
  name: '',
  email: '',
  password: '',
}

function App() {
  const handleSubmit = (values: UserFormValues) => {
    console.log('Form Submitted', values)
  }

  return (
    <div>
      <h1>User Registration</h1>
      <GenericForm 
        fields={userFormFields} 
        initialValues={initialValues} 
        onSubmit={handleSubmit} 
      />
    </div>
  )
}

在这个例子中,如果不使用泛型,你需要为每种类型的表单分别创建一个表单组件。使用泛型后,你可以创建一个通用的表单组件,可以用于任何类型的表单字段。这展示了泛型在 React 组件中的强大作用,使得我们的组件更加灵活和可复用。

附加示例:使用泛型创建通用的表格组件

在开发中,表格组件是一个常见的需求。为了使表格组件更加灵活和可重用,我们可以使用 TypeScript 泛型来创建一个通用的表格组件。通过这种方式,我们可以确保数据类型的一致性,并能够轻松地渲染不同类型的数据。

创建通用表格组件

首先,我们定义一个通用表格组件 Table,它接受一组行数据和一个用于渲染行的函数。

代码语言:javascript
复制
import React from 'react'

const Table = <TRow extends Record<string, any>>(props: {
  rows: TRow[];
  renderRow: React.FC<TRow>;
}) => {
  return (
    <table>
      <tbody>
        {props.rows.map((row, index) => (
          <props.renderRow key={index} {...row} />
        ))}
      </tbody>
    </table>
  )
}

在这个例子中,Table 组件接受一个泛型参数 TRow,它表示表格中每一行的数据类型。组件接收一个 rows 数组和一个 renderRow 函数。renderRow 函数负责渲染每一行的数据。

使用通用表格组件

接下来,我们使用 Table 组件来渲染一个包含姓名和年龄的表格。

代码语言:javascript
复制
import React from 'react'
import { Table } from './Table'

type Person = {
  name: string
  age: number
}

const people: Person[] = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 40 },
]

const RenderPersonRow: React.FC<Person> = ({ name, age }) => (
  <tr>
    <td>{name}</td>
    <td>{age}</td>
  </tr>
)

const App = () => {
  return (
    <div>
      <h1>People Table</h1>
      <Table rows={people} renderRow={RenderPersonRow} />
    </div>
  )
}

export default App

在这个例子中,我们定义了一个 Person 类型,它包含 name 和 age 两个属性。然后我们创建了一个 people 数组,包含两个人的姓名和年龄。RenderPersonRow 是一个用于渲染每行数据的组件,它接受 Person 类型的属性并返回一个表格行。

我们在 App 组件中使用 Table 组件,将 people 数组作为 rows 传递,并将 RenderPersonRow 作为 renderRow 函数传递给 Table 组件。这样,表格组件就会渲染包含两行数据的表格,每行数据对应一个人的姓名和年龄。

结束

TypeScript 的泛型是一项强大的功能,能够使你的 React 组件更加灵活和可重用。通过使用泛型,你可以创建适用于任何数据类型的组件,这在处理各种数据类型的实际应用中尤为有用。

希望这篇文章能让你更好地理解如何在 React 组件中使用泛型,并让你的组件变得更加灵活和可重用。如果你有任何问题或反馈,欢迎在评论区留言与我互动。

别忘了关注我的公众号「前端达人」,获取更多有趣的技术文章和实用的开发技巧。期待与你分享更多的编程知识,我们下期再见!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、利用 TypeScript 泛型创建简单的可重用 React 组件
    • 创建一个简单的泛型 React 组件
      • 使用泛型组件渲染字符串
        • 使用自定义类型的数据
          • 使用泛型组件渲染任务列表
          • 二、使用泛型在 React 组件中展示数据
            • 创建一个用于获取数据的泛型 React 组件
              • 使用 FetchAndDisplay 组件获取和展示帖子
                • 使用 FetchAndDisplay 组件获取和展示用户
                • 三、使用泛型创建通用的 React 表单组件
                  • 定义表单字段和组件的类型
                    • 创建通用表单组件
                      • 使用通用表单组件
                      • 附加示例:使用泛型创建通用的表格组件
                        • 创建通用表格组件
                          • 使用通用表格组件
                          • 结束
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档