What is a hook

A hook is a function that is used to hook a behavior before it is executed

Let me draw it as a graph

The left is the normal function execution flow, and the right is the execution flow with hook

Before hooks, how did we treat code reuse

Let’s assume a scenario where you are developing a backend management system. In this backend management system, most of them are query forms. Although their business is different, they have certain logical similarities

Common elements: indicates the loading status of data related to pages in the query condition list

These four characteristics are present in almost every form, which means that you need to repeat the logic for each form you write. There are two solutions to this problem in VUe2

  • Copy the same logical code every time
  • Use Vue mixins for logical commonality

However, there are problems with both approaches

  • Copying code is inefficient and, although relatively stable, requires a lot of coding and testing
  • vue mixinsLogical abstraction can be achieved, but this scheme is too simple and crude scheme, data global sharing, too much usemixinIt will cause the project to go out of control, which is a relatively dangerous scheme

Assume the form page data structure is as follows

{
  data: [].// List data
  currentPage: 1.// Current page
  pageSize: 0.// Number of pages per page
  thePageSize: 0.// The number of pages
  totalPages: 0./ / the total number of pages
  totalSize: 0.// Total number of records
}
Copy the code

In addition to the data returned from the back end, we also need to add some status values to the page to optimize the user experience

status: 'idle' | 'loading' | 'error' | 'success'|...// The current list request status
Copy the code

The above data + state exists in every background form page, which means repeated declarations, repeated docking, and repeated tests

Copying code in a seemingly stable way is actually unstable, because repeated logic is tested every time, and developers tend to be resistant to such tasks because they are tedious and repetitive

There is no very good solution to this problem in VUe2. For this problem, react, from mixin to HOC, and finally to hook, provides a good solution.

Now that @vue/reactivity provides hook capabilities, we can use hooks in Vue to optimize questions

Logic sharing is done through hooks

Following the above scenario, we now assume that we have three background form pages to write

Traditional coding scheme (without hook)

It can be seen that there are many similarities in the three page processes we need to write. In connection with data, although most of the codes are the same, we still need to write repeated tests, such as page number correlation and state correlation logic

Use hooks to perform repetitive logical abstractions

Each call to useHook generates a separate state

Although state and logic depend on the component, useHook can be defined outside of the component in a way that was previously impossible

Although the details of internal logic are different from page to page, the overall process and data structure are the same. Based on this premise, we can abstract for commonality, and then no matter how many pages we use abstract logic, we only need to maintain the abstract logic

Hooks are not exclusive to VUE3

Hook support is added in vue3 version, which does not mean vue3 is bound to hook. In fact, hook functionality provided in Vue3 can be used in any front-end application, namely @vue/reactivity

npm i @vue/reactivity
Copy the code

So vuE3, VUe2, React, and even HTML can use this NPM package

For a concrete implementation of Reactivity, check out my previous article on the reactivity source code

Actual Combat (useReqList)

For ease of understanding, the following code is vue2.x, written in JS

UseReqList is a custom hook that helps us abstract the public logic of the background form. Let’s take a look at the situation when it is not used

Unused useReqList

// ...
data() {
  return {
    // ...
    shopList: []./ / data
    homePage: 0.// How much data is on this page
    allPage: 0.// How many pages in total
    totalSize: 0.// How many entries in total
    pageSize: 0.// The maximum number of entries per page
    currentPage: 1.// Current page number}},methods: {init() {
      this.loading = true
      const data = {
        currentPage: this.currentPage,
        / /...
      }
      request(data) / / request
        .then((res) = > {
          this.shopList = res.data.data
          this.currentPage = res.data.currentPage
          this.pageSize = res.data.pageSize
          this.totalSize = Number(res.data.totalSize)
          this.homePage = res.data.thePageSize
          this.allPage = res.data.totalPages
        })
        .finally(() = > {
          this.loading = false}})},Copy the code

Using useReqList

import { useReqList } from '@/utils/hook/useReqList'
// ...
data() {
  return {
    dataList: useReqList()
  }
},
methods: {init() {
      const data = {
        currentPage: this.listData.currentPage || 1./ /...
      }
      this.listData.run(request(data)) / / request}},// let { data, currentPage, pageSize, thePageSize, totalPages, totalSize, status } = this.listData
Copy the code

As you can see from the above example, after using useReqList

The common variables, the common logic is abstracted away, and the page code is purer

Because of the nature of reactivity, they are all reactive

Avoid repeated coding, repeated testing, save development time

Hook useReqList (Custom Hook useReqList

UseReqList implementation

The internal implementation is the encapsulation of the common logic. The read data is processed by Reactive, and each declaration opens a separate piece of memory for the declarative unit to use because of the closure

import { reactive } from '@vue/reactivity'

/** * Abstraction for the backend form page logic *@returns* /
export function useReqList() {
  const defaultState = {
    data: [].// Table data
    currentPage: 1.// Current page
    pageSize: 0.// Number of pages
    thePageSize: 0.// The number of current pages
    totalPages: 0./ / the total number of pages
    totalSize: 0./ / the total number
    error: null.// Error cause
    status: 'idle'./ / the current state of idle | loading | success | error
  }

  letstate = reactive({ ... defaultState, })/** * Request successful *@param data* /
  const setData = (data) = > {
    state.currentPage = data.currentPage
    state.pageSize = data.pageSize
    state.thePageSize = data.thePageSize
    state.totalPages = data.totalPages
    state.totalSize = Number(data.totalSize)
    state.data = data.data
    state.status = 'success'
  }

  /** * Request failed *@param error* /
  const setError = (error) = > {
    state.data = []
    state.currentPage = 1
    state.pageSize = 0
    state.thePageSize = 0
    state.totalPages = 0
    state.totalSize = 0
    state.error = error
    state.status = 'error'
  }

  const setLoading = () = > (state.status = 'loading')
  const setPage = (num) = > {
    state.currentPage = num
  }

  const setReset = () = > {
    state.data = []
    state.currentPage = 1
    state.pageSize = 0
    state.thePageSize = 0
    state.totalPages = 0
    state.totalSize = 0
    state.error = null
    state.status = 'idle'
  }

  const run = (promise) = > {
    if(! promise || ! promise.then) {throw new Error('Please pass in data of type Promise')}if (['loading'].includes(state.status)) {
      return Promise.reject('Currently in request')
    }
    setLoading()

    return promise
      .then((res) = > {
        setData(res.data)
      })
      .catch((err) = > {
        console.log(err)
        setError(err)
      })
      .finally(() = >{})}return {
    run,
    setReset,
    setData,
    setPage,
    setLoading,
    setError,
    state,
  }
}
Copy the code

This package body is basically suitable for most vue2.x developed background management system projects. If you want to use it in the project, you only need to modify the setData part to adapt the back-end data

More vue hook functions

If vuE3 project, you can use hook tool library, VueUse, library provides a large number of common methods, digging the above has the introduction of the tool library article, according to the official website of the library is compatible with VUE2 document

🎩 VueUse works for Vue 2 & 3 within a single package by the power of vue-demi!
Copy the code

There is no test here. If you need it, you can try it

In what usages should a VUE project use hooks

About the Class API and Composition API

The transition from the Class /Option API to the Composition API was painful, and the two apis had very different ways of thinking

For those who have been writing Class and vue2. X for a long time, there are few obvious advantages to vue3 when they first come into contact with vue3. Even if they are not proficient in the setup syntax, they still code according to the thinking of Class and cannot give full play to the advantages of Hook. Class’s ability to organize code in a single file is almost beyond the setup syntax…

Later, the component gained some project experience and learned React Hook again, gradually gaining my own understanding of Hook. Hook is mainly used to separate logic from logic. In React and VUE, Hook can be used to analyze view from view, separate view from logic, and separate logic from logic. In this way, a single responsibility can be realized, and low coupling and high cohesion of logic and view can be achieved on this basis

Hook and Vuex

In a sense, Hook can replace Vuex. The REACT API useContext provides a similar function. However, it is easy for beginners to enter a misunderstanding when they just contact hook. Wrapping variables and logic in a hook and using it in multiple pages creates multiple Spaces in memory that are not shared and are consistent, which is a problem. For vue Hook style global state management libraries, I recommend Pinia

conclusion

After using the Setup syntax for a while, I gradually changed my view from cautious skepticism to embrace hook. The Composition API is good at logical abstraction, type derivation, and many other aspects. Although there are still some minor problems, I believe that you will develop an answer to them in the future. Custom Hook will be more and more popular, embrace Custom Hook,Vue3 bar!

If you encounter any problems in use, please go to QQ group 530496237 and blow water together

You can also add my wechat account: Carpediem-Rollin and join the wechat group