Server-Sent Events Request
use hook
Before using extension hooks, ensure you are familiar with the basic usage of alova.
Features
- More concise and user-friendly usage;
- Automatic connection management;
In [3.3.0+], this strategy is implemented using fetch, meaning you can specify headers, methods, and all other fetch-supported parameters for streaming requests. In earlier versions, it was implemented using EventSource.
Usage
import { useSSE } from 'alova/client';
const postMethodHandler = (value: string) => alova.Post('/api/source', null, {
headers: { 'Content-Type': 'application/json' },
param: { key: value }
});
const {
// Received data, updated with each reception
data,
// Current EventSource instance
eventSource,
// Connection state: 0-connecting, 1-open, 2-closed
readyState,
// Bind connection events
onOpen,
// Bind message reception
onMessage,
// Bind error events
onError,
// Bind custom events
on,
// Connect and send messages
send,
// Close the connection
close
} = useSSE(postMethodHandler, {
credentials: 'include',
initialData: 'initial-data', // Initial data for `data`
// ...other fetch parameters
});
Sending Requests
By default, requests are not sent automatically. You need to call send to initiate the request, or set immediate = true to send the request immediately.
const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(
postMethodHandler,
{
immediate: true
}
);
Each useSSE can only create one connection. This means that when attempting to connect to multiple targets, the previous connection will always be disconnected.
const { data, eventSource, readyState, onMessage, onError, on, send, close } =
useSSE(postMethodHandler);
send('value1');
send('value2'); // This will disconnect the previous connection
send('value3'); // This will also disconnect the previous connection
Receiving Data
When data is received, it is automatically assigned to the data state. You can bind it directly in the view or listen to it for further actions. All listener binding functions support chaining.
- vue
- react
- svelte
- solid
<template>
<div>
<span v-if="readyState === 0">Connecting</span>
<span v-else-if="readyState === 1">Connected</span>
<span v-else-if="readyState === 2">Disconnected</span>
</div>
<div>Last received data: {{data}}</div>
<ul>
<li
v-for="item in dataList"
:key="item">
{{item}}
</li>
</ul>
<button @click="send">Connect</button>
<button @click="close">Close</button>
</template>
<script setup>
import { ref } from 'vue';
const dataList = ref([]);
const { data, readyState, close, send } = useSSE(postMethodHandler)
.onMessage(({ data }) => {
dataList.value.push(data);
})
.onError(error => {
// ...
});
</script>
import { useEffect, useState } from 'react';
const App = () => {
const [dataList, setDataList] = useState([]);
const { data, readyState, close, send } = useSSE(postMethodHandler)
.onMessage(({ data }) => {
setDataList(prevList => [...prevList, data]);
})
.onError(error => {
// ...
});
return (
<div>
<span>
{readyState === 0 ? 'Connecting' : readyState === 1 ? 'Connected' : 'Disconnected'}
</span>
<div>Last received data: {data}</div>
<ul>
{dataList.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<button onClick={send}>Connect</button>
<button onClick={close}>Close</button>
</div>
);
};
<script>
let dataList = [];
const { data, readyState, close, send } = useSSE(postMethodHandler)
.onMessage(({ data }) => {
dataList.push(newData);
data = newData;
})
.onError(error => {
// ...
});
</script>
<div>
<span
>{readyState === 0 ? 'Connecting' : readyState === 1 ? 'Connected' : 'Disconnected'}</span
>
<div>Last received data: {data}</div>
<ul>
{#each dataList as item}
<li>{item}</li>
{/each}
</ul>
<button on:click="{send}">Connect</button>
<button on:click="{close}">Close</button>
</div>
import { createSignal } from 'solid-js';
const App = () => {
const [dataList, setDataList] = createSignal([]);
const { data, readyState, close, send } = useSSE(postMethodHandler)
.onMessage(({ data }) => {
setDataList(prevList => [...prevList, data]);
})
.onError(error => {
// ...
});
return (
<div>
<span>
{readyState() === 0 ? 'Connecting' : readyState() === 1 ? 'Connected' : 'Disconnected'}
</span>
<div>Last received data: {data()}</div>
<ul>
<For each={dataList()}>{item => <li key={item}>{item}</li>}</For>
</ul>
<button onClick={send}>Connect</button>
<button onClick={close}>Close</button>
</div>
);
};
You can also listen for custom events to receive data.
const { on } = useSSE(postMethodHandler);
// The following code will listen for events with the field `event: update`
const offUpdate = on('update', event => {
console.log(event.eventSource); // Current EventSource instance
console.log(event.data);
});
The update event will be triggered when the following data is received:
event: update
data: xxx
Binding Events
useSSE provides a series of event binding methods, which return unbinding functions when bound.
const { onMessage, onError, onOpen, on, close } = useSSE(postMethodHandler);
// Corresponds to the `message` event of EventSource
const offMessage = onMessage(event => {
console.log(event.eventSource); // Current EventSource instance
console.log(event.data);
});
const offError = onError(event => {
console.log(event.eventSource); // Current EventSource instance
console.error('SSE error', event.error);
close();
});
// Unbind events
offMessage();
offError();
You can also bind events via chaining.
const { data, readyState, close } = useSSE(postMethodHandler)
.onMessage(event => {
console.log(event.eventSource); // Current EventSource instance
console.log(event.data);
})
.onError(event => {
console.log(event.eventSource); // Current EventSource instance
console.error('SSE error', event.error);
close();
});
Global Response Interception
By default, response data is captured by the Global Response Interceptor. If this is not your intended behavior, you can manually disable it.
const { data, readyState, onMessage, on } = useSSE(postMethodHandler, {
interceptByGlobalResponded: false // Now data will not be intercepted
});
Reconnection on Disconnect
When the connection is unexpectedly terminated or the server actively closes it, a reconnection will be triggered after 1 second by default. You can set the reconnection time on either the client or server side.
const { data, readyState, onMessage, on } = useSSE(postMethodHandler, {
reconnectionTime: 2000 // Reconnection will be triggered after 2 seconds
});
If you want to disable reconnection when closing the connection, handle it as follows:
Set reconnectionTime to 0
A reconnectionTime of 0 disables the reconnection mechanism.
useSSE(postMethodHandler, {
reconnectionTime: 0
});
Active Closure by Client
Calling the close method of useSSE will not trigger reconnection. Therefore, you can notify the client to actively close the connection via a custom event.
const { close } = useSSE(postMethodHandler).on('close', () => {
close();
});
Server-Side Declaration
The server can declare the reconnection time. For example, sending the following data will set the reconnection delay to 5 seconds, overriding the client's reconnectionTime setting.
retry: 5000
Thus, the server can also send retry: 0 to notify the client not to reconnect before closing the connection.
Type Declarations
const enum SSEHookReadyState {
CONNECTING = 0,
OPEN = 1,
CLOSED = 2
};
type SSEHookConfig = {
/**
* Passed to `new EventSource`
*/
withCredentials?: boolean;
/**
* Whether to intercept via alova's global responded interceptor
* @default true
*/
interceptByGlobalResponded?: boolean;
/**
* Initial data
*/
initialData?: any;
/**
* Whether to send the request immediately
* @default false
*/
immediate?: boolean;
};
type SSEReturnType<S, Data> = {
readyState: ExportedType<SSEHookReadyState, S>;
data: ExportedType<Data | undefined, S>;
eventSource: ExportedType<EventSource | undefined, S>;
/**
* Manually send the request. Automatically triggered when `immediate: true` is set.
* @param args Request parameters passed to the method
*/
send: (...args: any[]) => Promise<void>;
/**
* Close the connection
*/
close: () => void;
/**
* Register a callback for the EventSource `open` event
* @param callback Callback function
* @returns Unregister function
*/
onOpen(callback: SSEOnOpenTrigger): () => void;
/**
* Register a callback for the EventSource `message` event
* @param callback Callback function
* @returns Unregister function
*/
onMessage<T = Data>(callback: SSEOnMessageTrigger<T>): () => void;
/**
* Register a callback for the EventSource `error` event
* @param callback Callback function
* @returns Unregister function
*/
onError(callback: SSEOnErrorTrigger): () => void;
/**
* @param eventName Event name (default: `open` | `error` | `message`)
* @param handler Event handler
*/
on(
eventName: string,
handler: (event: AlovaSSEMessageEvent<S, E, R, T, RC, RE, RH>) => void
) => () => void;
};
Is using alova in your project? please tell me!