React中常见的TypeScript定义使用

Stella981
• 阅读 1110

前言

在我学习typescript时,想在react中使用typescript写代码,从头开始的时候是懵逼的,因为官方文档并没有使用typescript的教程,多是自己在网上查,自己看定义摸索

所以今天把我用过的,总结归纳一下,希望能帮助到同样在摸索的同学

以下代码react版本为16.13.1,在create-react-app官方typescript模版中无报错

类型简述

一些React的内置类型

  • React.ReactElement —— 使用React.createElement创建的,可以简单理解为React中的JSX的元素
  • React.ReactNode —— <div>xxx</div> xxx的合法类型
  • React.CSSProperties —— 组件内联的style对象的类型
  • React.RefObject —— React.createRef创建的类型,只读不可改
  • React.MutableRefObject —— useRef创建的类型,可以修改
  • ...

组件声明

组件声明分为类组件和函数组件

类组件

类组件使用的定义主要为React.Component<P,S>React.PureComponent<P,S,SS>

interface AppProps {
  value: string;
}
interface AppState {
  count: number;
}
class App extends React.Component<AppProps, AppState> {
  static defaultProps = {
    value: "",
  };
  state = {
    count: 0,
  };
}
复制代码

React.Component<P,S>这里的Pprops的类型,Sstate的类型,可以写为React.Component<AppProp>,因为state的类型会自己推断

在上面PureComponent中还有个SS,这个SSgetSnapshotBeforeUpdate的返回值

函数组件

函数组件定义的方式简单来看有两种,一种是使用React.FC,一种是直接给props写上定义

interface AppProps {
  value?: string;
}

const App: React.FC<AppProps> = ({ value = '', children }) => {
    // ...
};
复制代码

React.FC的意思是FunctionComponent,如果使用了React.FC,它在定义里就已经规定好了children的类型和函数的返回值,所以可以直接用children

如果是直接给props写上定义,就需要自己定义children的类型

interface AppProps {
  value?: string;
  children?: React.ReactNode; // 自己定义children的类型
}

function App({ value = "", children }: AppProps) {
  return <>{children}</>;
}
复制代码

使用function来定义而不是箭头函数的优点是可以使用泛型组件

Hooks声明

hooks的声明如果不知道如何用ts定义可以直接点进去看看

useState

useState可以使用泛型传参或者自动推断

const [state, setState] = useState(''); // state的类型为string,自动推断
const [state, setState] = useState<string>(); // state的类型为 string | undefined
// 给初值
const [state, setState] = useState<string | null>(null); // state的类型为 string | null
复制代码

useRef

useRef同样也会自动推断

const ref = useRef(""); // ref.current的类型为 string
// 泛型
type Value = { value: string };
const ref = useRef<Value>({ value: "" });
// ref为html元素
const ref = useRef<HTMLDivElement>(null);
return <div ref={ref} />;
复制代码

需要注意的是如果ref为元素,那么初始值得写个null才不会报错

useReducer

useReducer相对来说要写的更多一点,可以自动推断,所以不需要手动写泛型类型(其实我也不知道手动写怎么写Orz)

// state类型
interface ReducerState {
  value: string;
}
// action类型
interface AnyAction {
  type: string;
  [key: string]: any;
}
// reducer函数
const reducer: React.Reducer<ReducerState, AnyAction> = (state, action) => {
  switch (action.type) {
    default:
      return state;
  }
};
// 初始值
const initialState: ReducerState = { value: "" };

const [state, dispatch] = useReducer(reducer, initialState);
// state 的类型为 ReducerState
// dispatch 的类型为 React.Dispatch<AnyAction>
复制代码

Action也可以是多个不同的Action的联合类型

useImperativeHandle

useImperativeHandle这个钩子可以把内部方法通过ref暴露出去,用ts也是要写多一点,类型都需要标注清楚

所以需要使用到React.forwardRef,可以先看下一节

// props
interface AppProps {
  value: string;
}
// useImperativeHandle获取到ref的类型
interface Handle {
  get: () => string;
}

const App = React.forwardRef<Handle, AppProps>(({ value }, ref) => {
  // 定义
  useImperativeHandle(ref, () => ({
    get: () => `handle get value : ${value}`,
  }));
  return null;
});
// 使用
const handleRef = useRef<Handle>(null);
// handleRef.current?.get();
return <App value="hello" ref={handleRef} />;
复制代码

自定义hook需要注意的

有如下钩子

const useCustomHook = () => {
  const [state, setState] = useState("");
  const set = (value: string) => {
    if (!value) return;
    setState(value);
  };
  return [state, set];
};
// 使用
const [state, setState] = useCustomHook();
setState('hello') // This expression is not callabl
复制代码

自定钩子还需要定义返回值才行

- const useCustomHook = () => {
+ const useCustomHook = (): [string, (value: string) => void] => {
复制代码

React.forwardRef

React.forwardRef<T, P = {}>只需要传props的类型和ref的类型,第一个Tref的类型,Pprops的类型

const App = React.forwardRef<HTMLDivElement, AppProps>(({ value }, ref) => {
  return <div ref={ref} />;
});
// 使用
const ref = useRef<HTMLDivElement>(null);
return <App value="hello" ref={ref} />;
复制代码

React.ForwardRefRenderFunction

定义为该类型的函数可以放进React.forwardRef函数中作为参数

// 定义
const forwardRender: React.ForwardRefRenderFunction<
  HTMLDivElement,
  AppProps
> = ({ value }, ref) => {
  return <div ref={ref} />;
};
const App = React.forwardRef(forwardRender);
// 使用
const ref = useRef<HTMLDivElement>(null);
return <App value="hello" ref={ref} />;
复制代码

React.createContext

泛型有自动推断的功能,所以useContext就不需要再写上类型了

interface ContextType {
  getPrefixCls: (value: string) => string;
}

const context = React.createContext<ContextType>({
  getPrefixCls: (value) => `prefix-${value}`,
});

const App = () => {
  const { getPrefixCls } = useContext(context);
  getPrefixCls("App"); // prefix-App
  return null;
};
复制代码

React.cloneElement

如果使用的React.FC定义的组件,它的children类型默认是React.ReactNode,需要显式转为React.ReactElement

const App: React.FC = ({ children }) => {
  return React.cloneElement(children as React.ReactElement, { value: "hello" });
};
// 也可以覆写定义
const App: React.FC<{ children: React.ReactElement }> = ({ children }) => {
  return React.cloneElement(children, { value: "hello" });
};
复制代码

React.ComponentType

通过React.ComponentType<P>定义的组件可以将变量名传入组件,在组件内调用,高阶组件通常会使用

interface AppProps {
  value: string;
}
const App: React.FC<AppProps> = (props) => {
  return null;
};
// React.ComponentType定义组件
function HOC<T>(Component: React.ComponentType<T>) {
  return function (props: T) {
    return <Component {...props} />;
  };
}
const WrappedComponent = HOC(App);
// 调用
<WrappedComponent value="hello" />
复制代码

泛型参数的组件

泛型参数的组件是typescript2.9版本新增的,第一次看见是在ant-deisgn

一个很简单的例子就是Select组件

<Select<number>>
  <Select.Option value={1}>1</Select.Option>
  <Select.Option value={2}>2</Select.Option>
</Select>
复制代码

类组件的定义

// 定义泛型参数的组件
class GenericComponent<P> extends React.Component<P> {
  internalProp: P;
  constructor(props: P) {
    super(props);
    this.internalProp = props;
  }
  render() {
    return null;
  }
}

type Props = { a: number; b: string };

<GenericComponent<Props> a={10} b="hi" />; // OK
<GenericComponent<Props> a={10} b={20} />; // Error
复制代码

函数组件的定义

function GenericComponent<P>(props: P) {
  const internalProp = useRef(props)
  return null;
}
复制代码

函数组件写起来可简洁太多了…

事件处理

也是多种多样

const App = () => {
  // React.MouseEventHandler
  const onClick: React.MouseEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  // React.ChangeEventHandler
  const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  // React.FocusEventHandler
  const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  return <input onClick={onClick} onChange={onChange} onFocus={onFocus} />;
};
复制代码

如果有事件不清楚该如何定义类型,可以点组件上的事件名去看看定义

需要注意的是只有e.currentTarget才会有对应的元素类型,e.target是没有的,它的类型是EventTarget

总结

想不出还有啥了,想起来再补充

ts自带了一些工具泛型,比如OmitPick,在开发的时候还是有帮助,可以看看我以前的总结

最后,祝大家身体健康,工作顺利!


太惨了,tsx连代码高亮都没有,只能说还是prismjs好使,highlightjs不太行

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这