useHook to request, a extremely fast variant of request library
Tags: vue, react, axios, javascript
Published: 2023-11-03 10:26:40

Alova is a lightweight request strategy library designed to simplify interface management and usage. It consists of 2 parts:

1. **Declarative Implementation of Complex Requests:** It empowers developers to implement complex requests, such as request sharing, pagination, form submissions, and resumable uploads, using a declarative approach. This eliminates the need for writing extensive code, resulting in improved development efficiency, application performance, and reduced server load.

2. **Automatic API Management and Maintenance:** Alova can automatically generate comprehensive TypeScript types for your request functions. This allows for seamless integration between frontend and server, eliminating the need to refer to API documentation and providing type hints. Moreover, it notifies the frontend when server APIs are updated, preventing project deployments.

[There are many runnable examples here](https://alova.js.org/category/examples)

With Alova, all you need to do is select the appropriate useHook for your requests, making it a "lazy developer's request library". If you find alova helpful, please consider star on [GitHub repository](https://github.com/alovajs/alova).

## Join Our Community

If you have any questions or need assistance, you can join our communication or start a discussion on [GitHub repository](https://github.com/alovajs/alova/discussions) for support. If you encounter any issues, please submit them on [GitHub issues](https://github.com/alovajs/alova/issues), and we'll address them as soon as possible.

- [Join Discord](https://discord.gg/S47QGJgkVb)

We also welcome contributions. For more information, please visit [contribution guidelines](https://alova.js.org/contributing/overview).

> For tutorials and more on how to use Alova, feel free to explore the [alova documentation](https://alova.js.org/).

Now, let's take a look at how to actually send the request. In `alova`, there are three use hooks `useRequest`, `useWatcher`, and `useFetcher` to implement the timing of the request. They control when the request should be sent, and at the same time It will create and maintain stateful request-related data for us, such as `loading`, `data`, `error`, etc. There is no need to maintain these states independently. Let's understand them below.

This time we first understand the first use hook, **useRequest**, which means sending a request. When `useRequest` is executed, a request will be sent by default. It is the most commonly used method when obtaining initial data on the page, and it also supports Turn off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data.

## Initial data request

### vue

```html
<template>
  <!-- You can directly use data to render the todo list -->
  <div v-if="loading">Loading...</div>
  <div
    v-else-if="error"
    class="error">
    {{ error.message }}
  </div>
  <template v-else>
    <div v-for="todo in data">
      <div class="todo-title">{{ todo.title }}</div>
      <div class="todo-time">{{ todo.time }}</div>
    </div>
  </template>
</template>

<script setup>
  const {
    // loading is the loading status value, when it is loaded, its value is true, and it is automatically updated to false after the end
    // It is a value of type Ref, you can access it through loading.value, or directly bind to the interface
    loading,

    // Response data, also Ref value
    data,

    // request error object, Ref value, there is a value when the request is wrong, otherwise it is undefined
    error

    // Directly pass in the Method instance to send the request
  } = useRequest(todoListGetter, {
    // Before the request responds, the initial value of data
    initialData: []
  });
</script>
```

### react

```jsx
const App = () => {
  const {
    // loading is the loading status value, when it is loaded, its value is true, and it is automatically updated to false after the end
    // Its value is an ordinary boolean value, and the set function will be automatically called internally to update its value when the request state changes
    loading,

    // response data
    data,

    // request error object, there is a value when the request is wrong, otherwise it is undefined
    error

    // Directly pass in the Method instance to send the request
  } = useRequest(todoListGetter, {
    // Before the request responds, the initial value of data
    initialData: []
  });

  // You can directly use todoList to render the todo list
  if (loading) {
    return <div>Loading...</div>;
  } else if (error) {
    return <div class="error">{error.message}</div>;
  } else {
    return (
      <>
        <div v-for="todo in data">
          <div class="todo-title">{todo.title}</div>
          <div class="todo-time">{todo.time}</div>
        </div>
      </>
    );
  }
};
```

### svelte

```html
<script>
  const {
    // loading is the loading status value, when it is loaded, its value is true, and it is automatically updated to false after the end
    // It is a value of Writable type, it will be maintained internally
    loading,

    // response data
    data,

    // request error object, there is a value when the request is wrong, otherwise it is undefined
    error

    // Directly pass in the Method instance to send the request
  } = useRequest(todoListGetter, {
    // Before the request responds, the initial value of data
    initialData: []
  });
</script>

<!-- You can directly use todoList to render the todo list -->
{#if $loading}
<div>Loading...</div>
{:else if $error}
<div class="error">{ $error.message }</div>
{:else} {#each $data as todo}
<div>
  <div class="todo-title">{ todo.title }</div>
  <div class="todo-time">{ todo.time }</div>
</div>
{/each} {/if}
```

## Binding request callback

To set the request callback, you can also receive the callback setting function in the return parameter of useRequest, as follows:

```javascript
const {
  //...

  // successful callback binding
  onSuccess,

  // failure callback binding
  onError,

  // Complete the callback binding, the callback will be called on success or failure
  onComplete
} = useRequest(todoListGetter);
onSuccess(event => {
  console.log('The request is successful, the response data is:', event.data);
  console.log('The method instance of this request is:', event.method);
  console.log('is the response data from cache:', event.fromCache);
});
onError(event => {
  console.log('The request failed, the error message is:', event.error);
  console.log('The method instance of this request is:', event.method);
});
onComplete(event => {
  // event.status is success on success, error on failure
  console.log('The request is completed, the status is:', event.status);
  console.log('The method instance of this request is:', event.method);
  console.log('is the response data from cache:', event.fromCache);
  if (event.data) {
    console.log('request data:', event.data);
  } else if (event.error) {
    console.log('Error message:', event.error);
  }
});
```

### Caution

Throwing an error in `onSuccess` will trigger `onError`

## Manually send request

When you need to create a new todo item, you can turn off the default sending request first, switch to manually triggering the request, and receive the `send` function in useRequest to manually send the request, and the `send` function will return a Promise with response data Instance, it will change to resolve state after request response.

```javascript
const {
  //...
  // Function for manual sender request, call to send request
  send: addTodo
} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), {
  // When immediate is false, it is not emitted by default
  immediate: false
});

// Manually send the request
const handleAddTodo = () => {
  const newTodo = {
    title: 'New todo item',
    time: new Date().toLocaleString()
  };
  // The send function returns a Promise object that can receive the response data
  addTodo(newTodo)
    .then(result => {
      console.log('The new todo item is successfully added, and the response data is:', result);
    })
    .catch(error => {
      console.log('Failed to add todo item, the error message is:', error);
    });
};
```

> `[2.9.0+]`In react, the send function is wrapped with `useCallback`, and it is not limited by the closure trap. You can use it directly in the event without worrying about performance problems.

## Force send request

Caching data can improve application fluency and reduce server pressure, but there is also the problem of data expiration. When you want to penetrate the cache to obtain the latest data, you can set the `force` property in the configuration of use hooks. help you.

### Set static value

force is false by default. When set to true, the cache will be penetrated every time and a request will be sent

```javascript
useRequest(alovaInstance.Get('/todo'), {
  force: true
});
```

### Dynamically set the force value

In actual situations, we often need to set whether to force the request to be sent according to different situations. At this time, force can be set as a function, which can be passed in through the send function.

```javascript
const { send } = useRequest(alovaInstance.Get('/todo'), {
  force: id => {
    return !!id;
  }
});
send(1);
```

## Send function parameter passing rules

In the above example, the send function is called to manually trigger the request, which can accept any number of parameters, which will be received by the following 4 functions:

### Receive in the useRequest callback function

It can be received when the first parameter of useRequest is set as a callback function, which is usually useful when deleting list items, as follows:

```javascript
const { send } = useRequest(id => removeTodoPoster(id));
send(1); // id in the above callback function will receive 1
```

### Received in onSuccess, onError, onComplete callback functions

`event.sendArgs` in onSuccess, onError, and onComplete callback functions are received in the form of an array

```javascript
const { send, onSuccess, onError, onComplete } = useRequest(newTodo => alovaInstance.Post('/todo', newTodo));
onSuccess(event => {
  // The value of sendArgs is [1]
  console.log(event.sendArgs);
});
onError(event => {
  // The value of sendArgs is [1]
  console.log(event.sendArgs);
});
onComplete(event => {
  // The value of sendArgs is [1]
  console.log(event.sendArgs);
});

// send request
send(1);
```

### Received in the force function

```javascript
const { send } = useRequest(alovaInstance.Get('/todo'), {
  force: id => {
    return !!id;
  }
});
send(1);
```

## Set initial response data

Before a page obtains the initial data, it inevitably needs to wait for the response from the server. Before the response, it is generally necessary to initialize the state to an empty array or an empty object, so as not to cause an error on the page. We can use the second in `useRequest` parameter to set the initial data.

```javascript
// Set initial data in useRequest
const {
  // The initial value of data before the response is [], not undefined
  // highlight-start
  data
} = useRequest(todoListGetter, {
  initialData: []
});
// highlight-end
```

## Manually update the states

In alova, various states such as `data`, `loading`, and `error` returned by `useRequest` allow custom modification, which will become very convenient in some cases.

### vue

```javascript
const { data, loading, error, update } = useRequest(todoListGetter);

//...
// update the data value directly
data.value = {};

// or use function update
update({
  data: {}
});
```

### react

In react, the returned state is data that can be used directly, so it needs to be modified by the `update` function.

```javascript
const { data, loading, error, update } = useRequest(todoListGetter);

//...
// update the data value by update
update({
  data: {}
});
```

### svelte

In svelte, the status returned by `useRequest` is of type `writable`.

```javascript
const { data, loading, error, update } = useRequest(todoListGetter);

//...
// change the data value directly
$data = {};
// or data.update(d => ({}));

// or use function update
update({
  data: {}
});
```

### Caution

1. The custom modified value will be overwritten by the internal state management mechanism of `useRequest`, for example, when you modify the `data` value, the `data` value will be assigned the latest response data after requesting again;
2. The state value modified directly will not modify the cached data synchronously. If you need to modify the cached data synchronously, it is recommended to use [updateState](https://alova.js.org/tutorial/learning/update-response-data-across-modules)

## Abort request manually

When the `timeout` parameter is not set, the request will never time out. If you need to manually abort the request, you can receive the `abort` method when the `useRequest` function is called.

```javascript
const {
  //...
  // highlight-start
  // abort function is used to abort request
  abort
  // highlight-end
} = useRequest(todoListGetter);

// highlight-start
// Call abort to abort the request
const handleCancel = () => {
  abort();
};
// highlight-end
```

> `[2.9.0+]`In react, the abort function is wrapped with `useCallback`, and it is not limited by the closure trap. You can use it directly in the event without worrying about performance problems.

`[2.6.2+]`In addition, this `abort` function will also be bound to the current method instance, so you can also call `method.abort` to abort this request.

```javascript
useRequest(todoListGetter);

// highlight-start
// Calling abort on the method can also abort the current request
const handleCancel = () => {
  todoListGetter.abort();
};
// highlight-end
```

`[2.6.2+]`You can also call `abort` in `beforeRequest` to abort the request.

```javascript
const alovaInst = createAlova({
  //...
  beforeRequest(method) {
    if (someCondition) {
      method.abort();
    }
  }
});
```

## Why Use Alova

Alova is committed to addressing client-side network request challenges, but what sets it apart from other request libraries is its focus on business-scenario-driven request strategies. When used in conjunction with libraries like `axios/fetch api`, Alova can meet 99% of your request needs while offering a range of advanced features.

- You may have pondered over whether to encapsulate `fetch` and `axios`, but now you no longer need to. With Alova, you can use a declarative approach to fulfill complex requests, including request sharing, pagination, form submissions, and resumable uploads, as well as automated cache management, request sharing, and cross-component state updates.

- Alova is lightweight, occupying only 4kb+, which is just over 30% of Axios's size.

- It currently supports `vue/react/react-native/svelte` and SSR frameworks like `next/nuxt/sveltekit`, as well as cross-platform frameworks like `Uniapp/Taro`.

- Alova is loosely coupled, allowing you to use it in any JavaScript environment with any UI framework using different adapters. It offers a unified user experience and seamless code migration.

- Alova also promotes a highly organized approach for aggregating API code, grouping request parameters, cache behavior, and response data transformations in the same code blocks, which is advantageous for managing numerous APIs.

[Compare Alova with other request libraries](https://alova.js.org/tutorial/get-started/comparison)

## Multi-Framework Support

Now, you can perfectly use Alova in [vue options (vue2 and vue3) syntax](https://vuejs.org/guide/introduction.html#api-styles). In the future, we plan to support the following frameworks:

- Functional ones like `solid/preact/qwik`.
- Class-based ones like `angular/lit/stencil`.
- Options-based ones like `native Chinese mini-programs`.

Alova also offers powerful request strategies:

| Name | Description | Documentation |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Pagination request strategy | Automatically manage paging data, data preloading, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50% | [usePagination](https://alova.js.org/tutorial/strategy/usePagination) |
| Non-sense data interaction strategy | A new interactive experience, submission and response, greatly reducing the impact of network fluctuations, allowing your application to still be available when the network is unstable or even disconnected | [useSQRequest](https://alova.js.org/tutorial/strategy/sensorless-data-interaction/overview) |
| Form submission strategy | A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset | [useForm](https://alova.js.org/tutorial/strategy/useForm) |
| File upload strategy | A simpler file upload strategy that supports automatic identification and conversion of base64, Blob, ArrayBuffer, and Canvas data | [useUploader](https://alova.js.org/tutorial/strategy/useUploader) |
| Send verification code | Verification code sending hook reduces the complexity of developing the verification code sending function. | [useCaptcha](https://alova.js.org/tutorial/strategy/useCaptcha) |
| Automatically re-pull data | Automatically re-pull data under certain conditions to ensure that the latest data is always displayed. | [useAutoRequest](https://alova.js.org/tutorial/strategy/useAutoRequest) |
| Trigger requests across components | An alova middleware that eliminates component-level restrictions and quickly triggers the operation function of any request in any component | [actionDelegationMiddleware](https://alova.js.org/tutorial/strategy/actionDelegationMiddleware) |
| UseRequest for serial requests | A more concise and easy-to-use serial request use hook than [alova's serial request method](https://alova.js.org/tutorial/next-step/serial-request), providing a unified loading status, error, callback function | [useSerialRequest](https://alova.js.org/tutorial/strategy/useSerialRequest) |
| UseWatcher for serial requests | A more concise and easy-to-use serial request use hook than [alova's serial request method](https://alova.js.org/tutorial/next-step/serial-request), providing a unified loading status, error, callback function. | [useSerialWatcher](https://alova.js.org/tutorial/strategy/useSerialWatcher) |
| Request retry strategy | Automatic retry on request failure, which plays an important role on important requests and polling requests | [useRetriableRequest](https://alova.js.org/tutorial/strategy/useRetriableRequest) |
| SSE requests | Requests via Server-sent Events | [useSSE](https://alova.js.org/tutorial/strategy/useSSE) |

For more in-depth learning about Alova, please visit the [Alova documentation](https://alova.js.org/). If you find Alova helpful, please star on [GitHub repository](https://github.com/alovajs/alova).

If you find this article helpful, don't hesitate to like and comment. Share your thoughts on Alova or ask any questions you may have. Your support is our greatest motivation!

### Join Our Community

If you have any questions, you can join our community chat groups or start discussions on the [GitHub repository](https://github.com/alovajs/alova/discussions). If you encounter any issues, please submit them on [GitHub's issues page](https://github.com/alovajs/alova/issues), and we will address them promptly.

- [Join our Discord community for discussions](https://discord.gg/S47QGJgkVb)

We also welcome contributions. For more information, please visit our [contribution guidelines](https://alova.js.org/contributing/overview).