实战用 Custom Hook + TS泛型实现 useArray
Posted 程序边界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实战用 Custom Hook + TS泛型实现 useArray相关的知识,希望对你有一定的参考价值。
文章目录
一、题目
完善自定义 Hook —— useArray ,使其能够完成 tryUseArray 组件中测试的功能:
- 入参:数组
- 返回值:
- value:最新状态的数组;
- add:添加元素;
- removeIndex:移除数组特定位置的元素;
- clear:清空数组;
相关文件代码:
- src\\utils\\index.ts
import useEffect, useState from "react";
export const useMount = (cbk: () => void) => useEffect(() => cbk(), []);
export const useArray = () => ;
- src\\tryUseArray.tsx
import useArray, useMount from "utils";
const TryUseArray = () =>
const persons: name: string; age: number [] = [
name: "jack", age: 25 ,
name: "ma", age: 22 ,
];
const value, clear, removeIndex, add = useArray(persons);
useMount(() =>
// 期待这里报错:Property 'notExist' does not exist on type ' name: string; age: number; []'.
// console.log(value.notExist);
// 期待这里报错:Property 'age' is missing in type ' name: string; ' but required in type ' name: string; age: number; '.
// add( name: "david" );
// 期待这里报错:Argument of type 'string' is not assignable to parameter of type 'number'.
// removeIndex("123");
);
return (
<div>
/*期待: 点击以后增加 john */
<button onClick=() => add( name: "john", age: 22 )>add john</button>
/*期待: 点击以后删除第一项*/
<button onClick=() => removeIndex(0)>remove 0</button>
/*期待:点击以后清空列表*/
<button style= marginBottom: "50px" onClick=() => clear()>
clear
</button>
value.map((person, index) => (
<div key=index style= marginBottom: "30px" >
<span style= color: "red" >index</span>
<span>person.name</span>
<span>person.age</span>
</div>
))
</div>
);
;
export default TryUseArray;
- src\\App.tsx
import "./App.css";
import TryUseArray from "tryUseArray";
function App()
return (
<div className="App">
<TryUseArray />
</div>
);
export default App;
答
答
答
案
案
案
在
在
在
后
后
后
面
面
面
,
,
,
没
没
没
有
有
有
完
完
完
成
成
成
不
不
不
要
要
要
偷
偷
偷
看
看
看
哦
哦
哦
!
!
!
二、答案(非标准)
import useEffect, useState from "react";
// 我的练习作业
// export const useArray = <T>(array: T[]) =>
// const [value, setValue] = useState(array)
// const clear = () => setValue([])
// const removeIndex = (index: number) => setValue([...value].filter((item, _index) => _index !== index))
// const add = (item: item) => setValue([...value, item]))
// return
// value, clear, removeIndex, add
//
//
export const useArray = <T>(array: T[]) =>
const [value, setValue] = useState(array);
return
value,
add: (item: T) => setValue([...value, item]),
removeIndex: (index: number) =>
const temp = [...value];
temp.splice(index, 1);
setValue(temp);
,
clear: () => setValue([]),
;
;
三、关键知识点
1.Custom Hook
官方文档:自定义 Hook – React
关键点
- 定义 Custom Hook 是一个函数,名字必须以 use 开头
- hook 只能在 React 函数组件 或其他 Hook 函数中调用(普通
js/ts
函数中不可用) - 相同的 Hook 不共享 state (重用状态逻辑的机制,所有 state 和副作用都是完全隔离的)
- 不要在循环,条件或嵌套函数中调用 Hook(建议在 Hook 内部使用循环,条件或嵌套函数)
- React 16.8+ 中使用
案例
useMount
- 封装
export const useMount = (cbk: () => void) => useEffect(() => cbk(), []);
- 调用
import useMount from "utils";
const [list, setList] = useState([]);
useMount(() =>
fetch(`$apiUrl/list`).then(async (res) =>
if (res.ok)
setList(await res.json());
);
);
useDebounce
- 封装
/**
* @param 值 val
* @param 延时:默认 1000 delay
* @returns 在某段时间内多次变动后最终拿到的值(delay 延迟的是存储在队列中的上一次变化)
*/
export const useDebounce = <V>(val: V, delay: number = 1000) =>
const [tempVal, setTempVal] = useState(val);
useEffect(() =>
// 每次在 val 变化后,设置一个定时器
const timeout = setTimeout(() => setTempVal(val), delay);
// 每次在上一个 useEffect 处理完以后再运行(useEffect 的天然功能即是在运行结束的 return 函数中清除上一个(同一) useEffect)
return () => clearTimeout(timeout);
, [val, delay]);
return tempVal;
;
- 调用
import useDebounce from "utils";
// 对 param 进行防抖处理
const lastParam = useDebounce(param);
const [list, setList] = useState([]);
useEffect(() =>
fetch(
// name=$param.name&personId=$param.personId
`$apiUrl/projects?$qs.stringify(lastParam)`
).then(async (res) =>
if (res.ok)
setList(await res.json());
);
, [lastParam]);
注意区别于 节流
拓展学习:
2.TS 泛型
官方文档:
关键点
- 不预先指定其具体的类型,而在使用的时候再进行定义
- 函数是对“值”的编程,泛型是对“类型”的编程
- 泛型是类型的变量
拓展学习:
ts类泛型接口扩展
ts定义类
es5的类是这样定义的
es6类是这样定义的
ts中也是类似
实例化的属性需要在一开头定义,
二缺一也不行。
ts的继承
也是使用extends跟super实现继承。
类里面的修饰符
ts中提供三种修饰符
public 公有 哪里都可以访问,
protected: 保护类型,在类子类可以访问,类外不行,
private:私有,只能在自己类里访问,其他对方访问报错。
属性不加修饰符默认就是公有
该属性公共,在哪都可以访问
该属性受保护,只能在类或者子类使用
该属性私有,只能在自己的类里面访问
对方法也是一样的。
静态属性,静态方法
es5中静态属性静态方法
其实可以这样理解,Son虽说是函数,本质也是对象,下面的操作是直接在Son这个对象上加属性,而不会被new后分配给实例,因为他们是Son对象上的属性和方法。而函数里面的this.run就是实例方法,会被实例继承的。
ts的类的静态方法
只需要在方法前加上static,定义是静态方法,不会被实例继承。
静态属性也是加static定义。
多态
父类定义一个方法不实现,让继承其的子类实现,每个子类有不同的表现
父类定义run方法,每个继承的子类实现该方法。这种写法只是表明多态是什么,结合抽象类能具体实现
抽象类 absrtact,要求子类比如包含某方法(抽象方法只能在抽象类使用)
父类不能实现该方法,交由子类实现
这时候必须子类必须实现抽象方法。
子类必须实现抽象父类的抽象方法,抽象顾名思义就是不具体的,所以子类必须给个具体的说法。
函数类接口
对函数传入的参数以及返回值进行约束
这就是一个基本的函数接口,直接对传入的参数以及返回值进行约束。相当于我们直接在后面写上类型()=>类型
这样约束。写成即接口类型可以复用。
可索引接口 数组 对象的约束
正常数组的约束
写成接口
因为数组的索引值是number类型的,所以适用于数组,而对象的属性都是string,
对对象的约束
类类型接口
与抽象类相似
类使用接口要用implements,此时接口就像一个抽象父类,里面的属性就像抽象属性方法,必须由子类具体实现
但这个抽象类的抽象方法的入参约束不到
接口扩展 接口继承接口这个主要用于封装组建的时候继承原生的HTML接口
使用的时候就可以传入原生的html属性
可以同时继承并且定义类的接口,因为name属性已经在父类定义了,所以super继承后,只需要定义eat方法就可。
泛型
泛型使得一个组件可以支持多个数据类型,提高复用性以及对不特定类型的支持。
基本使用
泛型类,类也可以作为参数传入泛型
如图,类可以作为参数传入,为什么不用接口呢?因为接口无法实例化
泛型接口
根据这些基本用法不难看出泛型可以大大提供复用性。相当于加了类型检验的any。
多个参数
案例 实现一个简单的可以操作数据库的苦库,支持mysql mongodb
首先应该想到使用接口约束参数,使用泛型来实现可复用性。
定义接口
定义操作类
类要以上面的接口为约束。
操作用户表
获取数据
演示基本的操作,更多复杂的操作可以自己实现,主要是定义接口和实现类接口,规范约束。
ts 命名空间
模块化主要侧重代码的复用,比如一个模块可能有好几个命名空间,命名空间主要是用来组织代码,防止命名冲突。
最基本的用法
ts的namespace转化成js就是函数自执行函数
也可以暴露出去
在别的文件就可以通过A.XX来调用。
ts装饰器
装饰器是一种特殊类型的声明,能够被附加到类声明,方法,属性或者参数上,可以修改类的行为,通俗的讲就是装饰器是一个方法,可以注入到类,属性,方法来扩展这些功能,
常见的装饰器有类 属性 方法 参数装饰器。
装饰器的写法有 普通装饰器(无法传参) 装饰器工厂(可以传参)
类装饰器,应用于构造函数,可用来监视替换修改类定义
普通装饰器(无法拆参)
在装饰器里面可以对类进行操作,就类似于react的高阶组件,这个类通过装饰器这个"高阶组件",功能更丰富了。如图
可以在装饰器在原型上增加一些属性方法供实例继承。
装饰器工厂 可传参 (之后的属性,方法等都是装饰器工厂
这种就是装饰器工厂。
类装饰器胡子哎沟站杉树运行时被调哟干嘛,类的构造函数作为其唯一的参数,但是如果,类装饰器返回了一个值,那么就会使用返回的值替代构造函数。
正常
使用装饰器
返回一个子类,该子类在继承父类实例的时候可以扩展自己的功能,也相当于react的高阶组件。
属性装饰器 接受两个参数 (原型对象 属性名称)
属性装饰器
attr就是属性的名称,target就是这个类,如图aa已经被修改了。传入的参数就是params,返回一个函数。
要注意这里的target跟类装饰器的target有点区别。
一个是构造函数,一个是整个类,因为本质来说,属性装饰器只是该属性的一个“高阶组件“,而类是整个类的”高阶组件”,所以不同。
方法装饰器
方法装饰器会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。在运行时传入三个参数,1 构造函数/类原型对象, 2 成员名字 3 成员属性描述符
当方法为static静态方法时
拿到的target就是构造函数
以上是关于实战用 Custom Hook + TS泛型实现 useArray的主要内容,如果未能解决你的问题,请参考以下文章
React+hook+ts+ant design封装一个input和select搜索的组件
组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList
React Custom Hook set 返回的函数不是函数
[React] Detect user activity with a custom useIdle React Hook