关键词

react结合typescript 封装组件实例详解

下面是 “react结合typescript 封装组件实例详解”的完整攻略。

一、为什么要使用TypeScript

TypeScript 是 JavaScript 的一个超集,它可以为 JavaScript 提供类型检查和其他一些新特性。TypeScript 具有以下优点:

  • 代码更加健壮,更容易维护。
  • 更好的智能提示和 IDE 支持。
  • 更容易对代码进行重构。
  • 更好的语言规范约束,可以防止出现一些常见的错误。
  • 易于与其他类型需要 TypeScript 支持的框架交互。

二、React 和 TypeScript 结合使用

为了可以更好的使用 React TypeScript,我们需要安装依赖包。

npm install react react-dom @types/react @types/react-dom typescript --save-dev

以上命令会安装 React 和 TypeScript 以及必要的类型定义文件。

接下来,在你的项目中创建一个 src 文件夹,并且创建一个 App.tsx 文件。在这个文件中,我们可以使用 TypeScript 和 React 构建我们的组件。以下是一个简单的例子:

import * as React from 'react';

interface Props {
    name: string;
}

const App: React.FC<Props> = ({ name }) => {
    return (
        <div>
            <h1>Hello, {name}!</h1>
        </div>
    );
};

export default App;

在上面的代码中,我们定义了一个 Props 的接口,它规定了组件需要传入一个名为 name 的属性。然后,我们使用 React.FC 类型定义了一个函数组件,名称为 App,它接受一个 Props 参数,并返回一个 JSX 元素。

三、如何封装 TypeScript 组件

较为常见的做法是,把类组件和函数式组件的特定结构合并在一起,这有助于保证每个组件都具有一致的 API。

这里我们以类组件的封装为例,将完成一个带计时器的数字输入框扩展按钮组件。

  • 定义属性类型 Props
interface Props {
  /** 输入框值 */
  value?: string;
  /** 输入框value的变化回调 */
  onChange?: (val: string) => void;
  /** 输入框通用属性 */
  [key: string]: any;
  /** 自动计时时长,单位毫秒 */
  autoCountDuration?: number;
}

以上属性中 autoCountDuration 是可选属性,其他以及通用属性皆为必填。

  • 定义组件状态 State
interface State {
  count: number;
}
  • 类组件实现
class InputBox extends React.Component<Props, State> {
  /** 计时器 */
  private timer?: NodeJS.Timeout;
  /** 计数器 */
  private count = 0;

  constructor(props: Readonly<Props>) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidUpdate(
    prevProps: Readonly<Props>,
    prevState: Readonly<State>
  ) {
    const { autoCountDuration } = prevProps;
    if (this.props.autoCountDuration !== autoCountDuration) {
      // 清除旧时器
      clearTimeout(this.timer!);
      if (typeof this.props.autoCountDuration === 'number') {
        this.startIntervalCount();
      }
    }
  }

  /**
   * 自动计时数数,使用类模式
   */
  private startIntervalCount() {
    this.count = 0;
    this.timer = setInterval(() => {
      this.count += 1;
      this.setState({ count: this.count });
    }, this.props.autoCountDuration!);
  }

  /**
   * 值变化事件
   *
   * @param e
   */
  private onChange(e: React.ChangeEvent<HTMLInputElement>) {
    const { onChange } = this.props;
    if (onChange) {
      onChange(e.target.value);
    }
  }

  /** 点击按钮 */
  private onBtnClick() {
    alert('你点击了扩展按钮!');
  }

  render() {
    const { count } = this.state;
    const { value, ...rest } = this.props;
    const btnStyle: React.CSSProperties = {
      marginLeft: '8px',
    };
    return (
      <div>
        <input
          {...rest}
          value={value || ''}
          onChange={this.onChange.bind(this)}
        />
        <button style={btnStyle} onClick={this.onBtnClick.bind(this)}>
          操作
        </button>
        <span>自动计数:</span>
        <span>{count}</span>
      </div>
    );
  }
}

上述代码中,我们首先声明了一个 class 类组件的名称和一个泛型接口 Props,它是一个对象,它用于存储类组件的属性。接着,在定义组件内部状态的声明。

在组件的构造函数中,我们用 super(props) 调用 React.Component 的构造函数,并使用 componentWillMount 设置本地 state。由于 constructor 仅能使用 super 和 state,所以它不适合进行本地状态初始化,因此我们采用 componentWillMount 进行设置。

这里我们需要注意的是,在 componentWillUnmount 函数中一定要记得清除 this.timer,以免造成内存泄露。

四、示例

下面我们来看一个示例:实现一个 TodoList 组件,包括添加和删除条目的功能。

import React, { useState } from 'react';

export type TodoItemType = {
  id: string;
  text: string;
}

interface Props {
  initData?: TodoItemType[];
}

const TodoList: React.FC<Props> = ({ initData = [] }) => {
  const [todoList, setTodoList] = useState<TodoItemType[]>(initData);
  const [textInputVal, setTextInputVal] = useState('');

  const handleTextInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    const val = e.target.value;
    setTextInputVal(val);
  };

  const handleAddTodoItem = () => {
    setTodoList((preList) => [
      ...preList,
      {
        id: String(Date.now() + Math.random() * 1000),
        text: textInputVal,
      },
    ]);
    setTextInputVal('');
  };

  const handleDeleteTodoItem = (id: string) => {
    setTodoList((preList) => preList.filter((item) => item.id !== id));
  };

  return (
    <div>
      <h2>TodoList</h2>
      <div>
        <input value={textInputVal} onChange={handleTextInput} />
        <button onClick={handleAddTodoItem}>添加</button>
      </div>
      <ul>
        {todoList.map((item) => (
          <li key={item.id}>
            <span>{item.text}</span>
            <button onClick={() => handleDeleteTodoItem(item.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

上述代码中,我们首先定义了一个 TodoItemType 类型,它描述了一个待办事项的结构。接着,我们定义了一个 Props 接口,它规定了组件需要传入一个名为 initData 的属性,它是一个 TodoItemType 数组类型,作为组件初始化的列表数据。

在组件中,我们使用 useState Hook 定义了 todoListtextInputVal 状态。然后,我们将 textInputVal 绑定到 <input> 元素,这样我们就能够实时得到用户输入的文本信息。

在添加函数 handleAddTodoItem 中,我们使用 setTodoList 进行列表的更新。通过展开运算符将前面的列表数据和新增数据合并为一个新的数组并更新到状态中。

最后,我们使用了 map 函数,渲染出列表中的各个项。每一项都会有一个删除按钮,点击时会触发 handleDeleteTodoItem,将待删除的项从待办事项列表中移除。

五、总结

本文介绍了如何利用 TypeScript 封装 React 组件。在这个例子中,我们定义了一个 <InputBox> 组件,并为其添加了计时器和扩展按钮功能,以及一个 <TodoList> 组件,它实现了添加和删除待办事项的功能。在这个过程中,我们学习了如何使用 TypeScript 定义组件 Props 和 State,以及如何在组件中使用 Hook 状态管理数据。最后,我们展示了如何使用 TypeScript 编写 React 组件的注意事项和核心代码。

本文链接:http://task.lmcjl.com/news/10163.html

展开阅读全文