Kaioken useAsync hook

This commit is contained in:
SheetJS 2024-05-21 18:03:41 -04:00
parent 833e9363d8
commit 349cc16819
2 changed files with 126 additions and 10 deletions

@ -12,7 +12,7 @@ init:
.PHONY: dev
dev:
cd docz; npm run start -- --host=0.0.0.0; cd ..
cd docz; npm run start -- --host=0.0.0.0 --no-open; cd ..
.PHONY: serve
serve:

@ -137,7 +137,7 @@ When the file header is not known in advance, `any` should be used.
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
functions simplify state updates. They are best used in the function bodies of
`useEffect`[^2] and `useCallback`[^3] hooks.
`useAsync`[^2], `useEffect`[^3] and `useCallback`[^4] hooks.
A `useEffect` hook can download and update state when a person loads the site:
@ -213,6 +213,41 @@ useEffect(() => { (async() => {
</TabItem>
</Tabs>
:::info pass
For this particular use case (fetching a file once when the page loads), it is
strongly recommended to use the `useAsync` hook:
```ts
import { useAsync } from 'kaioken';
import { read, utils } from 'xlsx';
/* Fetch and parse the file */
// highlight-next-line
const [ pres, loading, error ] = useAsync<President[]>(async() => {
/* Download from https://docs.sheetjs.com/pres.numbers */
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
const ab = await f.arrayBuffer();
/* parse */
const wb = read(ab);
/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects
// highlight-start
/* return data -- essentially setting state */
return data;
// highlight-end
}, []);
```
SheetJS users reported that it is easier to reason about data fetching using the
`useAsync` pattern compared to the traditional `useEffect` jujutsu.
:::
#### Rendering Data
Kaioponents typically render HTML tables from arrays of objects. The `TR` table
@ -243,7 +278,7 @@ in the example JSX code:
The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
functions simplify exporting data. They are best used in the function bodies of
`useCallback`[^4] hooks attached to button or other elements.
`useCallback`[^5] hooks attached to button or other elements.
A callback can generate a local file when a user clicks a button:
@ -278,7 +313,84 @@ const exportFile = useCallback(() => {
#### Complete Kaioponent
This complete Kaioponent example fetches a test file and displays the data in a
HTML table. When the export button is clicked, a callback will export a file:
HTML table. When the export button is clicked, a callback will export a file.
Examples using `useAsync` and `useEffect` with `useState` are shown below:
<Tabs groupId="hook">
<TabItem name="async" value="useAsync">
```tsx title="src/SheetJSKaiokenAoO.tsx"
import { useAsync, useCallback } from "kaioken";
import { read, utils, writeFileXLSX } from 'xlsx';
interface President {
Name: string;
Index: number;
}
export default function SheetJSKaiokenAoO() {
/* Fetch and parse the file */
const [ pres, loading, error ] = useAsync<President[]>(async() => {
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json<President>(ws); // generate objects
return data;
}, []);
/* get state data and export to XLSX */
const exportFile = useCallback(() => {
const ws = utils.json_to_sheet(pres!);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
writeFileXLSX(wb, "SheetJSKaiokenAoO.xlsx");
}, [pres]);
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
{ /* generate row for each president */
pres && pres.map(pres => (<tr>
<td>{pres.Name}</td>
<td>{pres.Index}</td>
</tr>))
}
{ /* loading message */
!pres && loading && ( <tr><td colSpan="2">Loading ...</td></tr> )
}
{ /* error message */
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
}
</tbody><tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>);
}
```
:::note pass
Typically the JSX structure uses ternary expressions for testing status:
```jsx
const [ pres, loading, error ] = useAsync(async() => { /* ... */ });
return ( <>
{ pres ? (
<b>Data is loaded</b>
) : loading ? (
<b>Loading ...</b>
) : (
<b>{error.message}</b>
)
}
</> );
```
For clarity, the loading and error messages are separated.
:::
</TabItem>
<TabItem name="effect" value="useEffect + useState">
```tsx title="src/SheetJSKaiokenAoO.tsx"
import { useCallback, useEffect, useState } from "kaioken";
@ -323,6 +435,9 @@ export default function SheetJSKaiokenAoO() {
}
```
</TabItem>
</Tabs>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
@ -335,7 +450,7 @@ This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:---------|:---------|:-----------|
| `0.17.0` | `5.2.10` | 2024-04-20 |
| `0.17.0` | `5.2.11` | 2024-05-21 |
:::
@ -482,7 +597,7 @@ This demo was tested in the following environments:
| Kaioken | ViteJS | Date |
|:---------|:---------|:-----------|
| `0.17.0` | `5.2.10` | 2024-04-20 |
| `0.17.0` | `5.2.11` | 2024-05-21 |
:::
@ -565,7 +680,8 @@ will generate a workbook that can be opened in a spreadsheet editor.
</details>
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/usestate) in the Kaioken documentation.
[^2]: See [`useEffect`](https://kaioken.dev/docs/hooks/useeffect) in the Kaioken documentation.
[^3]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/useState) in the Kaioken documentation.
[^2]: See [`useAsync`](https://kaioken.dev/docs/hooks/useAsync) in the Kaioken documentation.
[^3]: See [`useEffect`](https://kaioken.dev/docs/hooks/useEffect) in the Kaioken documentation.
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.
[^5]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.