Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/octet-stream/use-suspender
Wraps asynchronous function allowing to use it with React.Suspense
https://github.com/octet-stream/use-suspender
async javascript javascript-library preact promise react react-hooks react-suspense
Last synced: about 1 month ago
JSON representation
Wraps asynchronous function allowing to use it with React.Suspense
- Host: GitHub
- URL: https://github.com/octet-stream/use-suspender
- Owner: octet-stream
- License: mit
- Created: 2020-02-07T12:03:22.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2024-09-18T12:14:49.000Z (3 months ago)
- Last Synced: 2024-10-28T17:39:36.383Z (about 1 month ago)
- Topics: async, javascript, javascript-library, preact, promise, react, react-hooks, react-suspense
- Language: TypeScript
- Homepage: https://www.npmjs.com/use-suspender
- Size: 922 KB
- Stars: 17
- Watchers: 4
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: readme.md
- License: license
Awesome Lists containing this project
- fucking-awesome-react-hooks - `use-suspender` - api.html#reactsuspense) (Packages)
- awesome-react-hooks-cn - `use-suspender` - api.html#reactsuspense) (Packages)
- awesome-react-hooks - `use-suspender` - api.html#reactsuspense) (Packages)
- awesome-react-hooks - `use-suspender` - api.html#reactsuspense) (Packages)
README
# use-suspender
Wraps asynchronous function allowing to use it with [`React.Suspense`](https://react.dev/reference/react/Suspense).
![ESLint](https://github.com/octet-stream/use-suspender/workflows/ESLint/badge.svg)
![CI](https://github.com/octet-stream/use-suspender/workflows/CI/badge.svg)
[![Code Coverage](https://codecov.io/github/octet-stream/use-suspender/coverage.svg?branch=master)](https://codecov.io/github/octet-stream/use-suspender?branch=master)## Installation
Using pnpm:
```sh
pnpm add use-suspender
```or yarn:
```sh
yarn add use-suspender
```Or npm:
```sh
npm i use-suspender
```## API
### `createSuspender(implementation[, ctx])`
Creates a new `useSuspender` hook for given function.
Takes following argmuents:
| Name | Type | Required | Default | Description |
|----------------|:----------------------------------------------------------------------------:|:--------:|:-----------:|-----------------------------------------------------|
| implementation | [`SuspenderImplementation`](#interface-suspenderimplementationtresult-targs) | Yes | – | A function to create `useSuspender` hook for |
| ctx | `unknown` | No | `undefined` | `thisArg` to use with each `useSuspender` hook call |Returns a function implementing [`SuspenderHook`](#interface-usesuspenderhooktresult-targs) interface.
### `interface SuspenderImplementation`
Implements arbitary function. For TypeScript users this will help to narrow types for implementation's result and its arguments.
Takes following type parameters:
| Name | Extends | Required | Default | Description |
|---------|:--------------------:|:--------:|:--------:|---------------------------------------------------|
| TResult | – | Yes | – | The result returned by *suspender implementation* |
| TArgs | `readonly unknown[]` | Yes | – | A list of implementation's arguments |For example, if you create a function that returns a `User` type, the `useSuspender` hook will expect the same exact arguments your function is taking and return the same type of the result:
```tsx
import {createSuspender} from "use-suspender"
import type {FC} from "react"interface User {
id: string
fullName: string
role: string
age: number
}async function getUserFromSomewhereById(userId: string): Promise {
const response = await fetch(`https://example.com/api/v1/json/users/${userId}`)return response.json()
}// This will create a function implementing UseSuspenderHook interface.
const useGetUser = createSuspender(getUserFromSomewhereById)
// => UseSuspenderHookconst Profile: FC = () => {
// This function will expect the same arguments with the same types as getUserFromSomewhereById
// In this example, if you call it with just a number - you will get an error from TypeScript.
const user = useGetUser("42")return (
{/* It will also return the same type as getUserFromSomewhereById, so you'll have autocompletions */}
Welcome, {user.fullName}!
)
}export default Profile
```### `interface UseSuspenderHook`
Implements suspender hook, returned by `createSuspender` function.
This interface is a *function* with additional properties.
When called, it executes *suspender implementation* with given arguments.
This function will throw a Promise to notify `React.Suspense`
and resolve a result from suspender.When called with the same argments, it will find *pending* operation by comparing cached arguments using [`fast-deep-equal`](https://npmjs.com/package/fast-deep-equal) and re-throw a promise to notify `React.Suspense` if matched any.
Takes following type parameters:
| Name | Extends | Required | Default | Description |
|---------|:--------------------:|:--------:|:--------:|---------------------------------------------------|
| TResult | – | Yes | – | The result returned by *suspender implementation* |
| TArgs | `readonly unknown[]` | Yes | – | A list of implementation's arguments |#### `UseSuspenderHook.useSuspender(...args: TArgs): TResult`
Executes *suspender implementation* with given arguments.
This function will throw a Promise to notify `React.Suspense`
and resolve a result from suspender.When called with the same argments, it will find *pending* operation by comparing cached arguments using [`fast-deep-equal`](https://npmjs.com/package/fast-deep-equal) and re-throw a promise to notify `React.Suspense` if matched any.
This function should be called inside of your React function component.
- args – arguments to call the suspender with
#### `UseSuspenderHook.callEarly(...args: TArgs): void`
Calls useSuspense early without throwing a Promise needed to notify `React.Suspense`.
- args – arguments to call the suspender with
## Usage
Minimal example:
```tsx
import {createSuspender} from "use-suspender"
import {createRoot} from "react-dom/client"
import {Suspense} from "react"type Nationalities =
| "br"
| "ca"
| "ch"
| "de"
| "dk"
| "es"
| "fi"
| "fr"
| "gb"
| "ie"
| "in"
| "ir"
| "mx"
| "nl"
| "no"
| "nz"
| "rs"
| "tr"
| "ua"
| "us"const useGetRandomUser = createSuspender((nationality: Nationalities) => (
fetch(`https://randomuser.me/api?results=1&nat=${nationality}`)
.then(response => response.json())
.then(([result]) => result)
))function User() {
const user = useGetRandomUser("ua")return (
Name: {user.name.first} {user.name.last}
Email: {user.email}
)
}const App = () => (
Loading...}>
)const root = document.querySelector("#root")
createRoot(root).render()
```The `useSuspender` hook can take arguments to use in each suspender function call.
Imagine you have some API method, called `getUserByLogin`. It takes a user login
as the only argument. Here's an example of how you can apply this argument to the method:```js
import {createSuspender} from "use-suspender"
import {useParams} from "react-router-dom"import {getUserByLogin} from "./api/user"
// Alternative way to get useSuspender hook
const {useSuspender: useGetUserByLogin} = createSuspender(getUserByLogin)function User() {
const {login} = useParams()// Will execute getUserByLogin method with user taken from react-router-dom
const user = useGetUserByLogin(login)return (
Name: {user.name.first} {user.name.last}
Email: {user.email}
)
}export default User
```