preface

I have been using react function components with React-Hook and typeScript to fulfill functional requirements for more than a year, and I have benefited a lot from the process of getting familiar with Hook. It has to be said that React-Hook uses declarative syntax to define relevant variables, and the code logic to introduce and control state independently of the way the function is stored can greatly reduce the coupling degree of the code. The state is defined in the function header, without this, there is no need to declare it repeatedly when referencing. Component abstractions make it very clear what part of the code needs to be extracted, and TypeScript provides static type checking, object interface declarations, function parameter type definitions, and the ability to define a paradigm when a method or object is declared, replacing it with the type passed in when it is actually called. Thus a program using a paradigm can be applied to a variety of different situations. Ts is an operation to inject js soul, with typeScript support, can greatly enhance the development experience of projects.

Custom useAxios hook function

During project development, we abstracted the repetitive code, maintained a common component or function, imported and used as needed, such as a common Ajax request function. Now with React -hook, one of the performances after accepting the concept of hook is that they like to encapsulate common methods into custom hook functions starting with use, and internally they are driven based on the top-level hook provided by React. Here is the useAxios hook function that I wrapped step by step during my project development.

Directly look at the effect, take you cloud suction cat –> codesandbox

// useAxios.ts

import { useState, useEffect, useRef } from 'react';
import axios, {
  AxiosRequestConfig,
  AxiosResponse,
  CancelTokenSource,
  Canceler,
} from 'axios';

interface useAxiosConfig extendsAxiosRequestConfig { cancelable? :boolean;
}

interfaceuseAxiosReturnObj<T = any> { response? : AxiosResponse<T>; loading:boolean;
  error: boolean; cancelFn? : Canceler; refetchFn:(params? :any) = > void;
}

// Represents a tuple with an optional Boolean array item and any number of subsequent elements of any type
type useAxiosDeps = [boolean? . any[]];const isPlainObject = (val: any): val is Object= > {return!!!!! val &&typeof val === 'object' && val.constructor === Object;
};

/ * * *@template T Request result type definition *@param { useAxiosConfig } Config request configuration, inherited from axios request configuration object *@param { boolean } Cancelable Specifies whether to cancel the request function. When set to true, a cancelFn method is received to cancel the request *@param { useAxiosDeps } Deps dependency array *@param { boolean } The first item of the deps[0] array is whether to initiate the initialization request *@return { useAxiosReturnObj } The return value * /

export constuseAxios = <T = any>( config: useAxiosConfig, deps: useAxiosDeps ): useAxiosReturnObj<T> => { const [loading, setLoading] = useState<boolean>(false); const [response, setResponse] = useState<AxiosResponse<T>>(); const [error, setError] = useState<boolean>(false); const { cancelable = false, ... axiosConfig } = config; const cancelRef = useRef<CancelTokenSource>(); if (cancelable) { cancelRef.current = axios.CancelToken.source(); } const fetch = (params? : any) => { if (! isPlainObject(params)) { params = {}; } setLoading(true); axios .request<T>({ ... axiosConfig, data: { ... axiosConfig.data, ... params }, cancelToken: cancelRef.current? .token, }) .then((res) => setResponse(res)) .catch(() => setError(true)) .finally(() => setLoading(false)); }; useEffect(() => { const [initialRequest = true] = deps; if (initialRequest) { fetch(); } }, deps); useEffect(() => { return () => cancelRef.current? .cancel(); } []); const returnValues: useAxiosReturnObj = { response, loading, error, refetchFn: fetch, }; if (cancelable) { returnValues.cancelFn = cancelRef.current? .cancel; } return returnValues; };Copy the code

UseAxios accepts two arguments

Config Request Configuration

The parameter name The data type If required The default value describe
cancelable boolean no false Whether to cancel the request function, true will receive a cancenlFn method called cancenlFn to cancel the ongoing request

The config parameter also inherits the AxiosRequestConfig object from the AXIos request library and can be viewed for itself

Deps dependency array

The parameter name The data type If required The default value describe
arr[0] boolean no true The first argument to the array is whether to initiate an initialization request. False will not initiate the request when the component is first rendered

The return value

The parameter name The data type describe
response AxiosResponse<T> Suppose that the result of the template (T) definition response is passed when you declare useAxios, then the data property of the response object is the passed template. For AxiosResponse, pleaseTo view
loading boolean Whether the request is in progress, regardless of whether the data was successfully retrieved, is set to false after the request ends
error boolean Request error
cancelFn (message? : string): void If cancelable is set to true in the config parameter when useAxios is declared, this method can be called to cancel the ajax being requested during the request
refetchFn (params? : any) => void Call this method to re-initiate the request once, passing oneObject literalsParameter overrides the last request parameter

The sample

// Example.tsx

import React, { useState } from "react";
import { useAxios } from "./useAxios";

export interface CatObj {
  breeds: any[];
  id: string;
  url: string;
  width: number;
  height: number;
}

const Example = () = > {
  const [initialRequest, setInitialRequest] = useState(true);
  const {
    loading: catLoading,
    response,
    cancelFn,
    refetchFn,
    error
  } = useAxios<CatObj[]>(
    {
      cancelable: true.url: "https://api.thecatapi.com/v1/images/search".params: {
        size: "small".limit: 6}},/** * If you do not pass the initialRequest, you will initiate the initialRequest. In actual development, request parameters may need to be obtained dynamically. Call setInitialRequest to set the state to true * The useEffect dependency within useAxios changes accordingly and initiates a request for data */
    [initialRequest]
  );

  const handleClick = () = > {
    cancelFn && cancelFn();
  };

  if (error) {
    return <span>Request error</span>;
  }

  return (
    <>
      <button onClick={refetchFn}>The initiating</button>
      <button onClick={handleClick}>Cancel the request</button>
      <div>
        {catLoading ? (
          <span>loading...</span>
        ) : (
          <>{response? .data ? (<div
                style={{
                  display: "flex",
                  flexFlow: "row wrap",
                  alignContent: "flex-start}} ">
                {response.data.map((item) => (
                  <div style={{ flex: "0 0 33%" }}>
                    <div
                      key={item.id}
                      style={{
                        width: "100% ",height: "300px",
                        background: `url(${item.url}) center / cover no-repeat`}} ></div>
                  </div>
                ))}
              </div>
            ) : (
              <span>Blank default page</span>
            )}
          </>
        )}
      </div>
    </>
  );
};

export default Example;


Copy the code

The useAxios hook function above still has many places worth extending. For example, in actual development, we will define the basic response format of Response. data with the back end, then we can change the template of sending request to:

// useAxios.ts


// 1. Define a public response interface
interface responseData<T = any> {
  code: number;
  msg: string;
  data: T;
}

// 2. Modify the variable data type to receive the response
const [response, setResponse] = useState<AxiosResponse<responseData<T>>>();

// 3. Modify the template received by the request method when the request is initiated
axios.request<responseData<T>>({
  	// something code
  })

Copy the code

conclusion

The benefit of declarative syntax is very explicit code logic. The disadvantage is that it does not satisfy all cases, such as a scenario where the request parameters are assumed not to satisfy the request condition, and it may not even be necessary to declare useAxios. This method is useful for knowing exactly which requests to make when opening a page. At this point you can define it in the function header.

Early use of typeScript can also lead to a lot of confusion, not doing this or not doing that… “But once you get past that period of turbulence, your personal programming paradigm will be greatly improved, and it will stay with you through the boring and surprising discoveries of a programmer’s life.