一、题目
完善自定义 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+ 中使用
- Hook 规则 – React
案例
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]);
注意区别于 节流
拓展学习:
- 【笔记】Custom Hook
2.TS 泛型
官方文档:
- TypeScript: Documentation - Generics
- 泛型(generic) - TypeScript 中文手册
关键点
- 不预先指定其具体的类型,而在使用的时候再进行定义
- 函数是对“值”的编程,泛型是对“类型”的编程
- 泛型是类型的变量
拓展学习:
- 【笔记】TS 泛型