目录
- 1,介绍
- 2,简单举例
- 2.1,获取数据
- 1.2,计时器
- 2,自定义 HOOK 相比类组件
1,介绍
将一些常用的,跨组件的函数抽离,做成公共函数也就是 HOOK。自定义HOOK需要按照HOOK的规则来实现,才能像 useEffect
一样的使用。
可以看到,和项目中常用的工具方法
utils
类似,不同的是自定义HOOK 通常与业务强相关。
规则:
- 函数名必须以
use
开头; - 其他函数组件调用自定义HOOK函数时,应该放到顶层。
注意自定义HOOK 函数和函数组件的区别:要将函数当做组件来使用,函数名首字母必须大写。
2,简单举例
2.1,获取数据
import { useEffect, useState } from "react";
export const useList = () => {
const [list, setList] = useState([]);
useEffect(() => {
(async () => {
const res = await getFakerList();
setList(res);
})();
}, []);
return list;
};
const getFakerList = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([1, 2, 3]);
}, 1000);
});
};
使用,
import React from "react";
import { useList } from "./hooks";
export default function App() {
const list = useList();
const li = list.map((m) => <li key={m}>{m}</li>);
return <ul>{li}</ul>;
}
整体执行顺序:
- 函数组件调用
useList
函数- 执行
useEffect
副作用函数,请求接口获取数据。 - 返回状态变量
list
,此时获取的是空数组。
- 执行
useEffect
副作用函数,使用立即执行函数的原因:
想使用await
关键字,但useEffect
的第一个参数有要求,如果有返回则必须是普通函数,所以不能把async
关键字加到它上面。
type EffectCallback = () => (void | (() => void | undefined));
- 当模拟接口获取到数据,会更新状态变量
list
,触发更新。所以App.js
中会获取更新后的值渲染到页面。
可以将
useList
的函数体看做是在App.js
中,也就是说,声明的状态其实放到了App
函数组件中。
所以其实是App
函数组件重新渲染,触发了useList
函数重新调用。
1.2,计时器
import { useEffect } from "react";
export const useTimer = (func, duration = 1000) => {
useEffect(() => {
const timer = setInterval(func, duration);
return () => {
clearInterval(timer);
};
}, []);
};
使用时,只需要关心要执行的内容即可。
import React from "react";
import { useTimer } from "./hooks";
export default function App() {
useTimer(() => {
console.log("计时器的内容");
});
return <div>测试</div>;
}
2,自定义 HOOK 相比类组件
可以看到,通过HOOK实现的公共逻辑,如果在类组件中实现会比较繁琐,甚至会有到高阶组件才能实现。
而高阶组件虽然增强了类组件的功能,但带来了一些问题,比如 ref 转发,属性传递等,并且组件的嵌套层级也比较多,远不如 HOOK 来的方便。
以上。