An open API service indexing awesome lists of open source software.

https://github.com/ecomfe/react-suspense-boundary

A boundary component working with suspense and error
https://github.com/ecomfe/react-suspense-boundary

Last synced: 9 months ago
JSON representation

A boundary component working with suspense and error

Awesome Lists containing this project

README

          

# react-suspense-boundary

A boundary component working with suspense and error

Version 2.x is implemented on [use-sync-external-store](https://www.npmjs.com/package/use-sync-external-store) to align with future official suspense data fetching.

## Install

```shell
npm install react-suspense-boundary
```

## Demo

See online demo here: [https://ecomfe.github.io/react-suspense-boundary/](https://ecomfe.github.io/react-suspense-boundary/)

You can start demo app yourself by executing:

```shell
npm start
```

## Usage

### Basic

```jsx
import {Boundary, CacheProvider, useResource} from 'react-suspense-boundary';

// Create or import an async function
const fetchInfo = ({id}) => fetch(`/info/${id}`).then(response => response.json());

// Implement your presentational component
function Info({id}) {
// Call `useResource` to fetch, note the return value is an array
const [info] = useResource(fetchInfo, {id});

// There is no `loading` branch, push returned object immediately to render
return (


{info.id}: {info.name}

);
};

// Data is stored inside `CacheProvider`, suspending state is controlled with `Boundary`
export default function App() => (





);
```

### CacheProvider

`CacheProvider` is by its name a cache context where we store all resources loaded by its children.

The simpliest way to use `CacheProvider` is to provider an application level top cache:

```tsx
import {render} from 'react-dom';
import {CacheProvider} from 'react-suspense-boundary';
import {App} from './components/App';

render(


,
document.getElementById('root')
);
```

For some more complex applications, you may want to restrict data caching in a smaller scope, e.g. route level, and expire cached responses on unmount, you can put `CacheProvider` anywhere you want to make a shared cache.

### Boundary

`Boundary` components defines a boundary in your view, within a boundary all async resource fetchings and errors are collected to form a loading or error indicator.

Usually we would have mulitple `Boundary` inside a `CacheProvider`, that is, users see different sections loading individually, but all resources are shared.

A `Boundary` component receives props below:

```typescript
interface RenderErrorOptions {
recover: () => void;
}

interface BoundaryProps {
// When any of async progress is pending, boundary will render this element
pendingFallback: ReactNode;
// When any error are received, will render this function
renderError(error: Error, options: RenderErrorOptions): ReactNode;
// When any error are catched, will call this function
onErrorCaught(error: Error, info: ErrorInfo): void;
}
```

### useResource

The `useResource` hook is used to inspect an async function within a boundary:

```ts
type Resource = [
T,
{
expire(): void;
refresh(): void;
}
];

function useResource(action: (input: I) => Promise, params: I): Resource;
function useConstantResource(action: () => Promise): Resource;
```

Unlike other async hooks, `useResource` returns the result "immediately", there is no `pending` or `loading` state, no exception will throw.

Other than the result itself, the second object of `useResource`'s returned array is a a bunch of functions to manually control the cache:

- `expire` will immediately remove the cached result, causing the upper `Boundary` to be pending until `action` is resolved the next time.
- `refresh` is a function to run `action` again without removing previously cached result.

### Default configuration

`BoundaryConfigProvider` provides default configurations to `pendingFallback`, `renderError` and `onErrorCaught` props.

```javascript
import {Spin} from 'antd';
import {BoundaryConfigProvider} from 'react-suspense-boundary';

const defaultPendingFallback = ;

const defaultRenderError = error => (


{error.message}

);

const App = () => {

{/* All Boundary elements inside it receives default configurations */}

}
```

### Preload

Preload is much like resource fetching, they can be "immediately" fired within a render function:

```ts
function usePreloadResource(action: (input: I) => Promise, params: I): void;
function usePreloadConstantResource(action: () => Promise): void;
```

Preload fires resource fetching process but not abort current render.

You can also get a `preload` function using `usePreloadCallback` hook to preload any resources in effect or event handlers:

```tsx
const preload = usePreloadCallback();

preload(fetchList, {pageIndex: currentPageIndex + 1})}>
Next Page

```

## Create Your Own Cache

`react-suspense-boundary`'s built-in `CacheProvider` references a single context type, that is, you are unable to access multiple caches in a single component:

```tsx





```

By default, there is no way to a make `MyResource` to load one resource into the outer `CacheProvider` and another into the inner `CacheProvider`.

To solve this issue, we provide a `create()` function to create a custom set of providers and hooks, with a different context type so that you can use them simultaneously:

```tsx
import {CacheProvider, create, useConstantResource} from 'react-suspense-boundary';

const {
CacheProvider: GlobalCacheProvider,
useConstantResource: useGlobalConstantResource,
} = create();

function MyResource() {
// Put current user resource into global cache
const [currentUser] = useGlobalConstantResource(fetchCurrentUser);
// And other resources into local one
const [dataSource] = useConstantResource(fetchList);

return (
// ...
);
}



```

`create` function also accepts an option object to customize context's display name:

```ts
interface CreateOptions {
cacheContextDisplayName?: string;
configContextDisplayName?: string;
}
```