From 349cc16819ee6af4920594e983f923ecb5ae7299 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 21 May 2024 18:03:41 -0400 Subject: [PATCH] Kaioken `useAsync` hook --- Makefile | 2 +- docz/docs/03-demos/02-frontend/01-kaioken.md | 134 +++++++++++++++++-- 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index f37f0f1..44bf7a2 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/docz/docs/03-demos/02-frontend/01-kaioken.md b/docz/docs/03-demos/02-frontend/01-kaioken.md index 20661b6..3e7de30 100644 --- a/docz/docs/03-demos/02-frontend/01-kaioken.md +++ b/docz/docs/03-demos/02-frontend/01-kaioken.md @@ -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() => { +:::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(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(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: + + + + +```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(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(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 ( + { /* generate row for each president */ + pres && pres.map(pres => ( + + + )) + } + { /* loading message */ + !pres && loading && ( ) + } + { /* error message */ + !pres && !loading && ( ) + } +
NameIndex
{pres.Name}{pres.Index}
Loading ...
{error.message}
+ +
); +} +``` + +:::note pass + +Typically the JSX structure uses ternary expressions for testing status: + +```jsx +const [ pres, loading, error ] = useAsync(async() => { /* ... */ }); + +return ( <> + { pres ? ( + Data is loaded + ) : loading ? ( + Loading ... + ) : ( + {error.message} + ) + } + ); +``` + +For clarity, the loading and error messages are separated. + +::: + +
+ ```tsx title="src/SheetJSKaiokenAoO.tsx" import { useCallback, useEffect, useState } from "kaioken"; @@ -323,6 +435,9 @@ export default function SheetJSKaiokenAoO() { } ``` + +
+
How to run the example (click to hide) @@ -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.
-[^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.