React这样的框架存在对应的声明文件,声明文件通过一些基础语法定义了一些类型,从而获取到TypeScript中诸如类型检查、接口提示等特性功能,也可以在自己用TS开发时避免自己写一些复杂的ts结构类型了,下面就来看一下React中定义ts语法如何使用。
一、基础
俗话说的好:“授人以鱼不如授人以渔”,今天我也不知道自己的是“鱼”还是“渔”,就讲述一下自己这几天学习React语法的忐忑之路。
看typescript中文文档,然后总结了一波学习笔记。
总结完之后,发现ts里面有类型推断的能力,很多在React这样的框架项目上根本用不上呀!!!
开启网上的疯狂搜索功能,看看有没有关于React这样的文章,搜索了一下,确实有一些,讲述有哪些React独有的类型;
卧槽,难道我为了用Ts又要记这些新的API吗?这不是坑爹吗?
“柳暗花明又一村”,偶然的机会我点击了一个函数Reducer,神奇的发生了跳转,跳转到index.d.ts;
这不就是声明文件吗?然后认真分析Reducer;
type Reducer<S, A> = (prevState: S, action: A) => S;
- 1.
这不就是个函数的类型别名吗?其中两个S和A分别是泛型,然后返回值是S,那如果这样的话,我根本就不用记住很多这个类型了,当需要的时候直接点击该函数,跳转到对应的声明文件然后仔细研读一波就好了,哈哈,貌似就是这么回事。
【自己试了试,确实可以解决80%的问题】
不过为了提高开发效率,节省自己研究的成本,我还是写出几个常用的React中的ts语法,方便开发的时候套用。
二、 React中内置函数
React中内置函数由很多,我们就挑几个常用的来学习一下。
2.1 React.FC< P >
React.FC<>是函数式组件在TypeScript使用的一个泛型,FC就是FunctionComponent的缩写,事实上React.FC可以写成React.FunctionComponent。
import React from 'react';
interface demo1PropsInterface {
attr1: string,
attr2 ?: string,
attr3 ?: 'w' | 'ww' | 'ww'
};
// 函数组件,其也是类型别名
// type FC<P = {}> = FunctionComponent<P>;
// FunctionComponent<T>是一个接口,里面包含其函数定义和对应返回的属性
// interface FunctionComponent<P = {}> {
// // 接口可以表示函数类型,通过给接口定义一个调用签名实现
// (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
// propTypes?: WeakValidationMap<P> | undefined;
// contextTypes?: ValidationMap<any> | undefined;
// defaultProps?: Partial<P> | undefined;
// displayName?: string | undefined;
// }
const Demo1: React.FC<demo1PropsInterface> = ({
attr1,
attr2,
attr3
}) => {
return (
<div>hello demo1 {attr1}</div>
);
};
export default Demo1;
- 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.
2.2 React.Component< P, S >
React.Component< P, S > 是定义class组件的一个泛型,第一个参数是props、第二个参数是state。
import React from "react";
// props的接口
interface demo2PropsInterface {
props1: string
};
// state的接口
interface demo2StateInterface {
state1: string
};
class Demo2 extends React.Component<demo2PropsInterface, demo2StateInterface> {
constructor(props: demo2PropsInterface) {
super(props);
this.state = {
state1: 'state1'
}
}
render() {
return (
<div>{this.state.state1 + this.props.props1}</div>
);
}
}
export default Demo2;
- 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.
2.3 React.createContext、useContext、和useReducer中Ts使用
这三者经常一起使用,用来进行跨级组件间的数据传输,ts版如下所示:
- React.createContext
其会创建一个Context对象,当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的context值。【注:只要当组件所处的树没有匹配到Provider时,其defaultValue参数参会生效】
const MyContext = React.createContext(defaultValue);
const Demo = () => {
return (
// 注:每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化。
<MyContext.Provider value={xxxxxx}>
// ……
</MyContext.Provider>
);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- useContext
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 的 value prop 决定。语法如下所示:「const value = useContext(MyContext);」
import React, {useContext} from "react";
const MyContext = React.createContext('');
const Demo3Child: React.FC<{}> = () => {
const context = useContext(MyContext);
return (
<div>
{context}
</div>
);
}
const Demo3: React.FC<{}> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<MyContext.Provider value={'222222'}>
<MyContext.Provider value={'33333'}>
<Demo3Child />
</MyContext.Provider>
</MyContext.Provider>
);
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- useReducer
useState的替代方案,接收一个形如(state, action) => newState的reducer,并返回当前state以及其配套的dispatch方法。语法如下所示:「const [state, dispatch] = useReducer(reducer, initialArg, init);」
import React, {useReducer, useContext} from "react";
interface stateInterface {
count: number
};
interface actionInterface {
type: string,
data: {
[propName: string]: any
}
};
const initialState = {
count: 0
};
// React.Reducer其实是类型别名,其实质上是type Reducer<S, A> = (prevState: S, action: A) => S;
// 因为reducer是一个函数,其接受两个泛型参数S和A,返回S类型
const reducer: React.Reducer<stateInterface, actionInterface> = (state, action) => {
const {type, data} = action;
switch (type) {
case 'increment': {
return {
...state,
count: state.count + data.count
};
}
case 'decrement': {
return {
...state,
count: state.count - data.count
};
}
default: {
return state;
}
}
}
// React.createContext返回的是一个对象,对象接口用接口表示
// 传入的为泛型参数,作为整个接口的一个参数
// interface Context<T> {
// Provider: Provider<T>;
// Consumer: Consumer<T>;
// displayName?: string | undefined;
// }
const MyContext: React.Context<{
state: stateInterface,
dispatch ?: React.Dispatch<actionInterface>
}> = React.createContext({
state: initialState
});
const Demo3Child: React.FC<{}> = () => {
const {state, dispatch} = useContext(MyContext);
const handleClick = () => {
if (dispatch) {
dispatch({
type: 'increment',
data: {
count: 10
}
})
}
};
return (
<div>
{state.count}
<button onClick={handleClick}>增加</button>
</div>
);
}
const Demo3: React.FC<{}> = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<MyContext.Provider value={{state, dispatch}}>
<Demo3Child />
</MyContext.Provider>
);
};
export default Demo3;
- 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.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
三、React中事件处理函数
React中的事件是我们在编码中经常用的,例如onClick、onChange、onMouseMove等,那么应该如何用呢?
3.1 不带event参数
当对应的事件处理函数不带event参数时,这个时候用起来很简单,如下所示:
const Test: React.FC<{}> = () => {
const handleClick = () => {
// 做一系列处理
};
return (
<div>
<button onClick={handleClick}>按钮</button>
</div>
);
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
3.2 带event参数
老铁们可以试试,当事件处理函数待event参数的时候,如果不做任何处理,铁定报错,此时就按照第一节的方法论来试一试:
- 带上event参数,报错;
const Test: React.FC<{}> = () => {
// 报错了,注意不要这么写……
const handleClick = event => {
// 做一系列处理
event.preventDefault();
};
return (
<div>
<button onClick={handleClick}>按钮</button>
</div>
);
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 点击onClick参数,跳转到index.d.ts文件;
// onClick是MouseEventHandler类型
onClick?: MouseEventHandler<T> | undefined;
// 那MouseEventHandler<T>又是啥?原来是个类型别名,泛型是Element类型
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
// 那么泛型Element又是什么呢?其是一个接口,通过继承该接口实现了很多其它接口
interface Element { }
interface HTMLElement extends Element { }
interface HTMLButtonElement extends HTMLElement { }
interface HTMLInputElement extends HTMLElement { }
// ……
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 至此,就知道该位置应该怎么实现了;
const Test: React.FC<{}> = () => {
const handleClick: React.MouseEventHandler<HTMLButtonElement> = event => {
// 做一系列处理
event.preventDefault();
};
return (
<div>
<button onClick={handleClick}>按钮</button>
</div>
);
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
对于其它的事件一样,按照上述三个步骤,就可以一篇搞定,不需要进行所谓的死记硬背。
四、普通函数
普通函数是通用的,但是还是在这个位置提一下。
- 一个具体类型的输入输出函数;
// 参数输入为number类型,通过类型判断直接知道输出也为number
function testFun1 (count: number) {
return count * 2;
}
- 1.
- 2.
- 3.
- 4.
- 一个不确定类型的输入、输出函数,但是输入、输出函数类型一致;
// 用泛型
function testFun2<T> (arg: T): T {
return arg;
}
- 1.
- 2.
- 3.
- 4.
- async函数,返回的为Promise对象,可以使用then方法添加回调函数,Promise是一个泛型函数,T泛型变量用于确定then方法时接收的第一个回调函数的参数类型。
// 用接口
interface PResponse<T> {
result: T,
status: string
};
// 除了用接口外,还可以用对象
// type PResponse<T> = {
// result: T,
// status: string
// };
async function testFun3(): Promise<PResponse<number>> {
return {
status: 'success',
result: 10
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.