日期:2022年10月3日

自定义钩子

随着练习功能的增多,我们编写的React代码变得越来越复杂。像是上节课中我编写的React代码,仅仅是增加了一个加载数据的功能,我们就需要向App.js中引入了三个state和一个钩子函数:

const [stuData, setStuData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);\
useEffect(()=>{
    const fetchData = async () => {
        try{
            setLoading(true);
            setError(null);
            const res = await fetch('http://localhost:1337/api/students');
            if(res.ok){
                const data = await res.json();
                setStuData(data.data);
            }else{
                throw new Error('数据加载失败!');
            }
        }catch (e){
            setError(e);
        }finally {
            setLoading(false);
        }
    };
    fetchData();
}, []);

随着这种代码的增多App.js中的代码会变得越来越多,难以维护。所以我们就迫切的需要一个东西可以将这些代码存储起来,一来可以降低单个文件中的代码数量,二来也可以让这些代码方便在多个组件中复用。

但是问题就来了,这些代码无论是state还是effect其实都是钩子函数,但是钩子函数又不是说随便在哪都能写的,这要怎么处理呢?

我们在刚刚介绍钩子函数时就说过,钩子函数只能运行在函数组件或自定义钩子中,所以要提取这些代码我们只有一个选择,那就是自定义钩子。

自定义钩子是个什么玩意?它其实一点也不神秘,自定钩子就是一个普通的函数。普通函数怎么定义,它就怎么定义。但是它又不那么普通,因为钩子函数的名字必须以use开头,使用use开头后,React就能自动识别出它是一个钩子函数,这样才会以钩子函数的方式去处理它。

使用钩子

  1. 创建一个函数,命名为useXxx
  2. 在函数中正常调用React中的各种钩子
  3. 在组件中引用钩子

这个步骤看着是不是特别草率,但事实其实就这么一回事。当函数使用use开头后,React就允许我们在其中调用React的钩子函数,所以我们就可以通过自定义的钩子函数,将组件中的涉及到钩子的代码封装起来,方便调用。

将上述代码修改为自定义钩子:

src/hooks/useFetch.js

import {useEffect, useState} from "react";

const useFetch = (url) => {

    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    async function fetchData(){
        try{
            setLoading(true);
            setError(null);

            const res = await fetch('http://localhost:1337/api/students');
            if(!res.ok){
                throw new Error('数据加载失败!');
            }
            const data = await res.json();
            setData(data.data);
        }catch (e){
            setError(e);
        }finally {
            setLoading(false);
        }
    }
    return {data, loading, error, fetchData};
};

export default useFetch;

App.js调用钩子

const {data:stuData, loading, error, fetchData} = useFetch();

useEffect(()=>{
    fetchData();
}, [])

这样一来,将App.js中发送请求相关的钩子都编写到useFetch中,并将App中会用到的变量作为返回值返回,而作为App来说,只需要调用useFetch,即可获取到stuData、loading、error等数据以及fetchData函数,这样一来大大简化了App中的代码,同时使得其他组件也可以通过useFetch来发送请求加载数据。

等等,好像还有个问题,我们将请求地址在useFetch中写死了,难道我们只向一个地址发请求吗?这样做不合适吧?当然不合适,我们可以将钩子中那些会发生的变化的值作为参数传递,比如请求地址,如此一来我们就可以向任意地址发送请求啦!但是这里我我就不写了,自己试一试吧!

0 0 投票数
文章评分
订阅评论
提醒
guest

2 评论
最旧
最新 最多投票
内联反馈
查看所有评论
leeorz
leeorz
1 月 前

超哥,有些地方没明白。

如果我在两个不同的组件中,都调用了useFetch(),那么这两个组件中拿到的loading是同一个loading吗?我自己的试验,好像拿到的是同一个loading.

那么基于上面的结果,如果我在同一个组件中调用两次useFetch(),那么拿到的data应该是相同的变量.那么第二次api请求的结果就会覆盖第一次请求的结果.

所以这样封装不就会出问题了吗?

2
0
希望看到您的想法,请您发表评论x