react^18.2.0

例子

下面是一个很简单的一个 antd Input 的封装组件,我们希望它对外暴露一个 clear 方法,用于清空输入内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Input, InputProps, Space } from "antd";

export type UserInputPropsType = {
  size: InputProps["size"];
  value: string;
  onChange(val: string): void;
};

//  定义暴露方法的类型
export type UserInputHandler = {
  clear: () => void;
};

//  将类型传递给 forwardRef 的泛型 UserInputHandler
const UserInput = forwardRef<UserInputHandler, UserInputPropsType>(
  ({ size = "middle", value, onChange }: UserInputPropsType, ref) => {
    const [_value, setValue] = useState("");

    //  暴露方法
    useImperativeHandle(
      ref,
      () => ({
        clear() {
          setValue("");
          onChange("");
        },
      }),
      []
    );

    useEffect(() => {
      setValue(value);
    }, [value]);

    return (
      <Space size={12}>
        <Input
          style={{ width: 240 }}
          size={size}
          value={_value}
          onChange={(ev) => {
            setValue(ev.target.value);
            onChange(ev.target.value);
          }}
        />
      </Space>
    );
  }
);

//  这里需要提供下名称
//  不然 typescript 会报错,因为 forwardRef 无法找到组件名称
//  这是个 eslint 的规则 plugin:react/recommended
UserInput.displayName = "UserInput";

export default UserInput;

使用

来看下其它组件引用这个文件后该如何使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { ElementRef } from "react";
import { UserInput } from "./UserInput";

const Form = () => {
  //  这里需要用 ElementRef 来定义 UserInput 组件暴露的方法类型
  const UserInputRef = useRef<ElementRef<typeof UserInput>>(null);

  return (
    <div>
      <UserInput {...props} ref={UserInputRef} />
      {/*这里的 clear 已经有类型提示了*/}
      <button onClick={() => UserInputRef.current.clear()}>清空</button>
    </div>
  );
};