一、专栏介绍 🔩🔩
欢迎加入本专栏!本专栏将引领您快速上手React,让我们一起放弃放弃的念头,开始学习之旅吧!我们将从搭建React项目开始,逐步深入讲解最核心的hooks,以及React路由、请求、组件封装以及UI(Ant Design)框架的使用。让我们一起掌握React,开启前端开发的全新篇章!
二、自定义Hook 🔨🔨
React 有一些内置 Hook,例如 useState,useContext 和 useEffect。
在React中,自定义Hook的作用就是将组件之间的公共逻辑提取出来,以便在不同的组件中复用。这与我们在面向对象的编程中,将公共方法提取到一个新的类中并在多个对象之间共享这个类的方法是类似的。
自定义Hook可以帮助我们更好地组织和复用代码,提高代码的可读性和可维护性。使用自定义Hook,我们可以将组件的逻辑封装在一个独立的函数中,并在需要的时候调用这个函数。这样,我们就可以在不同的组件中共享这个逻辑,而不需要在每个组件中都重复编写相同的代码。
因此,你的理解是正确的,自定义Hook就是去提取和复用组件之间的公共逻辑。如果你想优化这段话,你可以这样说:“自定义Hook类似于我们在面向对象的编程中提取和复用公共方法,它可以帮助我们更好地组织和复用React组件之间的公共逻辑,提高代码的可读性和可维护性。”
这里推荐一个第三方hooks。ahooks - React Hooks Library - ahooks 3.0
这里面有很多,如果都没有满足你的,再去自定义,尽量用别人造好的轮子,人家能上线,肯定是经过了很多轮测试已经实际开发使用过的。比你自己造好太多了。
三、demo案例 💺💺
3.1、没有封装自定义Hook前 👇👇
比如下面的例子,我请求接口前需要显示加载中的状态,加载完成以后显示出用户列表的信息。以及我还得定义list这个State,代码看起来就很多。这样的操作可能很多地方都需要用到,那我们就可以把这个请求过程,以及请求结束的代码进行一个封装,让它变成一个自定义Hook。其他需要这个需求的地方都可以共用。
import React, { useState } from 'react';
const UseCustomDemo = () => {
type UserType = {
name: string;
age: number;
};
// 用户列表
const [list, setList] = useState<UserType[]>([]);
// 加载状态
const [loading, setLoading] = useState<boolean>(false);
const requestApi = () => {
return new Promise<UserType[]>((resolve) => {
resolve([
{ name: '张三', age: 26 },
{ name: '李四', age: 28 },
]);
});
};
const getData = () => {
setLoading(true);
requestApi().then((res) => {
setTimeout(() => {
setList(res);
setLoading(false);
}, 2000);
});
};
return (
<div>
{loading ? (
<div>加载中……</div>
) : (
<div>
{list.map((item, index) => (
<p key={index}>
姓名:{item.name}, 年龄:{item.age}
</p>
))}
</div>
)}
<button onClick={getData}>加载用户信息</button>
</div>
);
};
export default UseCustomDemo;
3.2、进行封装以后 👇👇
首先提取去能够共用的代码,并且在这基础之上扩展更多的功能。在项目src文件夹下创建Hooks文件夹。并且新建文件useRequestApi.tsx
useRequestApi.tsx代码
这个hook接收两个参数,第一个为api,我这里只是为了演示,根据自己实际情况去更改。第二个参数表示是否自动加载,如果为true,引用这个hook的时候就会执行,如果不是就需要在外部调用getData的方法去执行。
这里还有一个核心的地方就是<T>。使用泛型 <T>
的目的是为了使自定义Hook能够适应不同的数据类型。这意味着,根据接口返回的数据结构的不同,我们可以在使用自定义Hook的地方定义具体的类型,以便获取到正确的数据类型。通过传入泛型参数,自定义Hook能够根据参数类型返回相应的数据类型。例如,如果我们使用useRequestApi<string>
,那么返回的data
就会是string
类型。这样可以使代码更加灵活,并且在使用自定义Hook的地方可以更直观地获取到对应的数据接口。
import { useEffect, useState } from 'react';
// 正常来说,这里的url应该是一个后缀名,在这个hook里面会自动拼接前缀。然后发送到后端进行请求。
// 因为我这里没有实际的接口,然后也没有安装插件,我就用代码模拟一个请求。
export const useRequestApi = <T,>(url: string, automatic: boolean) => {
const [data, setData] = useState<T>();
const [loading, setLoading] = useState<boolean>(false);
const requestApi = (url: string) => {
return new Promise<any>((resolve, reject) => {
if (url === 'api') {
resolve([
{ name: '张三', age: 26 },
{ name: '李四', age: 28 },
]);
} else {
reject('404 没有找到此API');
}
});
};
const fetchData = async () => {
setLoading(true);
setTimeout(async () => {
const response = await requestApi(url);
setData(response);
setLoading(false);
}, 2000);
};
useEffect(() => {
if (automatic) {
fetchData();
}
}, [url]);
const getData = () => {
fetchData();
};
return { data: data || [], loading, getData };
};
hook写完以后,刚刚我们没有进行提取的代码就变成了下面的样子。是不是让我们的代码看起来的更加的简便,清爽,减少了定义useState的弊端,这样后期维护起来也比较容易。
import React from 'react';
import { useRequestApi } from '../Hooks/useRequestApi';
const UseCustomDemo = () => {
type UserType = {
name: string;
age: number;
};
const { data, loading, getData } = useRequestApi<UserType[]>('api', true);
return (
<div>
{loading ? (
<div>加载中……</div>
) : (
<div>
{data.map((item, index) => (
<p key={index}>
姓名:{item.name}, 年龄:{item.age}
</p>
))}
</div>
)}
<button onClick={getData}>加载数据</button>
</div>
);
};
export default UseCustomDemo;
四、总结 💪💪
自定义Hook使得我们可以在函数组件中复用和共享状态逻辑和其他React特性,这有助于提高代码的可读性和可维护性。
通过自定义Hook,我们可以将组件中的相互关联的部分拆分成更小的函数,例如设置订阅或请求数据等,这使得组件更加简洁和易于理解。
使组件更加简洁和可维护:使用自定义Hook可以将组件的逻辑封装在一个独立的函数中,并在需要的时候调用这个函数。这样,我们就可以在不同的组件中共享这个逻辑,而不需要在每个组件中都重复编写相同的代码。
总的来说,自定义Hook在React中是非常重要的,它为我们提供了一种更加灵活和可维护的方式来组织和复用代码逻辑,使我们的组件更加简洁和可维护。
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇