React Hooks入门:手写一个 useAPI

react-hooks 入门

写在最前面

  • 最近项目 升级了react 16.8+,接入了 hooks,这里学习一下最基础的几个官方 hookshtml

  • 下面是官网文档的连接,基础知识掌握不牢靠的朋友能够再看看,官网的文档能够说是很是完整和浅出了。个人文章主要讨论具体的几个 hooks 的具体使用场景。react

  • zh-hans.reactjs.org/docs/hooks-…ios

一、useEffect

  • 官方 demo
mport React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
  );
}
复制代码

useEffect 作了什么?git

  • 经过使用这个 Hook,你能够告诉 React 组件须要在渲染后执行某些操做。React 会保存你传递的函数(咱们将它称之为 “effect”),而且在执行 DOM 更新以后调用它。在这个 effect 中,咱们设置了 document 的 title 属性,不过咱们也能够执行数据获取或调用其余命令式的 API。

二、useCallback 和 useMemo

把内联回调函数及依赖项数组做为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。github

  • 为了节约内存,咱们能够把接口获取的数据先使用 useCallback 和 useMemo 作临时存储。这种优化有助于避免在每次渲染时都进行高开销的计算。
  • useCallback(fn, deps) 至关于 useMemo(() => fn, deps)
  • how to use useCallback
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

const getDownloadFile = useCallback(async () => {
    setLoading(true);

    try {
        const res = await axios.get(API.CUSTOMER.xxx(), {
            params: { customer_id: 123 }
        });

        setData(res.data as any);
    } catch (error) {
        setError(error);
    }

    setLoading(false);
}, []);
    
// 这里的 useEffect() 替代了之前的生命周期作的事情
useEffect(() => {
   getDownloadFile();
}, [getDownloadFile]);
复制代码

三、useContext()

咱们须要先建立一个 context 对象(React.createContext),接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。axios

当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。api

了解了上面解构 hooks 下面咱们来实战一下

## 这里咱们来写一个简单的 useAPI

import { useState } from 'react';
import axios from 'axios';

// 首先定义一下类型
type UseApiResponse = [
    {
        loading: boolean,

        data: null | object | any[],

        error:  any
    },
    /** * 返回一个 promise 对象 */
    (requestData?: any[] | object) => Promise<any>,
];

type UseApiArgs = {
    /** * HTTP Method. 默认 'GET' */
    method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' | 'OPTIONS',

    url: string,

    /** * 可选:初始默认值 */
    defaultData?: object | any[],

    /** * 返回数据 */
    bodyData?: object | any[],

};

export const useApi = ({
    method = 'GET',
    url,
    defaultData,
}: UseApiArgs) => {
    const [loading, setLoading] = useState(false);
    const [data, setData] = useState(defaultData | null)
    const [error, setError] = useState(null);
   
    const sendRequest = (requestData?: object | any[]) => {
        const requestConfig = {
            method,
            url,
            data: requestData,
        };

        const axiosConfig = Object.assign({}, requestConfig);

        /** * 返回一个 promise 对象 */
        return new Promise(async (resolve, reject) => {
            setLoading(true);
            try {
                const response = await axios(axiosConfig);
                setData(response.data);
                resolve(data);
            } catch (err) {
                setError(error);
                reject(err);
            } finally {
                setLoading(false);
            }
        });
    };

    const response: UseApiResponse = [
        {
            loading,
            data,
            error
        },
        sendRequest,
    ];
    return response;
};

export default useApi;
复制代码

上面知足了基本的调用server api 的需求,可是远远是不能知足一些复杂的状况的,咱们下面来升级一下咱们扥 hooks,增长状态码,增长加载状态,主动触发 request 的需求等等

升级版

import { createContext, useState, useEffect, useContext } from 'react';
import axios from 'axios';

/** * ApiContext 这里能够配置全局的 config. */
export const ApiContext = createContext({});

// 首先定义一下类型
type UseApiResponse = [
    {
        loading: boolean,

        data: null | object | any[],

        error: Error || null,

        /** * The HTTP status number */
        status: number,

        /** * True unless and until the request has been triggered at least once */
        initialLoad: boolean,

        /** * pending 状态 */
        pendingOrLoading: boolean,

        /** * axios 对象 */
        responseObj: object,
    },
    /** * 返回一个 promise 对象 */
    (requestData?: any[] | object) => Promise<any>,
];

type UseApiArgs = {
    /** * HTTP Method. 默认 'GET' */
    method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' | 'OPTIONS',

    url: string,

    /** * 可选:初始默认值 */
    defaultData?: object | any[],

    /** * 返回数据 */
    bodyData?: object | any[],

    /** * 可选 : 若是你想主动调用 request, 设置为 true */
    autoTrigger?: boolean,
};

export const useApi = ({
    method = 'GET',
    autoTrigger = true,
    url,
    defaultData,
    bodyData,
}: UseApiArgs) => {
    const [initialLoad, setInitialLoad] = useState(true);
    const [loading, setLoading] = useState(false);
    const [data, setData] = useState(defaultData || null)
    const [error, setError] = useState(null);
    const [status, setStatus] = useState();
    const [responseObj, setResponseObj] = useState();

    /** * 你可使用自定义的 api 来替代 Axios config */
    const globalConfig = useContext(ApiContext);

    const sendRequest = (requestData?: object | any[]) => {
        const requestConfig = {
            method,
            url,
            data: requestData,
        };

        const axiosConfig = Object.assign({}, globalConfig, requestConfig);

        /** * 返回一个 promise 对象 */
        return new Promise(async (resolve, reject) => {
            setLoading(true);
            try {
                const response = await axios(axiosConfig);
                setResponseObj(response);
                setData(response.data);
                setStatus(response.status);
                resolve(data);
            } catch (err) {
                setError(error);
                reject(err);
            } finally {
                setLoading(false);
                if (initialLoad) setInitialLoad(false);
            }
        });
    };

    /** * 若是设置了自动触发这个参数,这里须要特殊处理一下,检查一下 initiaload 加载状态是否完成,而后处理 'POST', 'PATCH', 'PUT' */
    if (autoTrigger) {
        useEffect(() => {
            if (initialLoad) {
                /** * Include body data if method allows */
                if (['POST', 'PATCH', 'PUT'].includes(method)) {
                    sendRequest(bodyData);
                } else sendRequest();
            }
        }, []);
    }

    const response: UseApiResponse = [
        {
            loading,
            data,
            error,
            status,
            initialLoad,
            pendingOrLoading: initialLoad || loading,
            responseObj,
        },
        sendRequest,
    ];
    return response;
};

export default useApi;

复制代码

怎么使用?

import React, { useEffect, useRef } from 'react';
import useApi, { ApiContext } from 'use-http-api';

const UserList = () => {
    const [{ loading, data }, getUsers] = useApi({
        url: 'https://reqres.in/api/users',
        defaultData: { data: [] },
    });

    return (
        <div> {loading ? ( 'Loading...' ) : ( <ol> {data.data.map(user => ( <li>{user.email}</li> ))} </ol> )} <button onClick={() => getUsers()}>Update</button> </div>
    );
};
复制代码

参考

相关文章
相关标签/搜索