hilla
hilla copied to clipboard
Introduce useOffline hook for making use of offline state easier
Describe your motivation
There is ConnectionState and ConnectionStateStore in "@vaadin/common-frontend", but the API is not very React friendly. Hence it would be nice to have useOffline hook.
Describe the solution you'd like
In my demo application I created the following custom hook, which can be used as a reference.
import { ConnectionState, ConnectionStateStore } from '@vaadin/common-frontend';
import { Signal, useSignal } from '@vaadin/hilla-react-signals';
import { useEffect } from 'react';
/**
* Represents the offline hook.
*/
type OfflineHook = {
offline: Signal<boolean>;
isOffline: () => boolean;
clearCache: () => void;
store: (key: string, value: any) => void;
get: (key: string) => any;
};
/**
* Represents a callback function that is invoked when the offline status changes.
* @param offline - A boolean value indicating whether the user is offline or not.
*/
type OfflineChangedListener = (offline: boolean) => void;
/**
* Represents the props for the Offline component.
*/
type OfflineProps = {
onOfflineChange?: OfflineChangedListener;
cacheName?: string;
};
/**
* Custom hook to handle offline state in the application.
* @param {OfflineProps} props - Optional props for the hook. Use { onOfflineChange : OfflineChangedListener } a callback function that is invoked when the offline status changes.
* @returns {OfflineHook} An object containing the offline state and a function to check if the application is offline.
*/
export function useOffline(props?: OfflineProps): OfflineHook {
const offline = useSignal(false);
let connectionStateStore: ConnectionStateStore | undefined;
let internalState = isOffline();
const CACHE_NAME = props?.cacheName ?? 'offline-cache';
// Listen connection state changes
const connectionStateListener = () => {
const newState = isOffline();
const changed = newState !== internalState;
offline.value = newState;
internalState = newState;
if (changed) {
props?.onOfflineChange?.(internalState);
}
};
useEffect(() => {
setupOfflineListener();
// Cleanup
return () => connectionStateStore?.removeStateChangeListener(connectionStateListener);
}, []);
/**
* Checks if the application is currently offline.
* @returns {boolean} Returns true if the application is offline, otherwise false.
*/
function isOffline() {
return connectionStateStore?.state === ConnectionState.CONNECTION_LOST;
}
/**
* Sets up an offline listener to handle changes in the connection state.
*/
function setupOfflineListener() {
const $wnd = window as any;
if ($wnd.Vaadin?.connectionState) {
connectionStateStore = $wnd.Vaadin.connectionState as ConnectionStateStore;
connectionStateStore.addStateChangeListener(connectionStateListener);
connectionStateListener();
}
}
/**
* Stores a value in the cache with the specified key.
*
* @param key - The key to store the value under.
* @param value - The value to store.
*/
function store(key: string, value: any) {
const cache = getCache();
cache[key] = value;
localStorage.setItem(CACHE_NAME, JSON.stringify(cache));
}
/**
* Retrieves the value associated with the specified key from the cache.
*
* @param key - The key of the value to retrieve.
* @returns The value associated with the specified key, or undefined if the key does not exist in the cache.
*/
function get(key: string): any {
const cache = getCache();
return cache[key];
}
function getCache(): any {
const cache = localStorage.getItem(CACHE_NAME) ?? '{}';
return JSON.parse(cache);
}
/**
* Clears the cache by removing the item with the specified cache name from the local storage.
*/
function clearCache() {
localStorage.removeItem(CACHE_NAME);
}
return { offline: offline, isOffline: isOffline, clearCache: clearCache, store: store, get: get };
}
Describe alternatives you've considered
Also I just noticed that there is cacheable.ts utility in the documentation, but that is not very React like either
https://hilla.dev/docs/react/guides/client-caching
One could add caching utility to useOffline and perhaps we should add something like the useOffline I did in Hilla/React itself. Note, cacheable utility is not enough, as you probably need that offline state variable in your UI as well as the listener for offline state change, e.g. if you want to purge saved items to server when coming back to online.
Additional context
No response
State is so 2020. I would suggest a signal instead.