Skip to content

Commit 79f2f15

Browse files
authored
docs: rewrite Vue integration guide around markRaw pattern (#1689)
## Summary - Replaces the `SpreadsheetProvider` class example with the idiomatic `markRaw` pattern as the primary recommendation - Reframes the SSR section: HyperFormula is SSR-safe by default, `<ClientOnly>` is an optional optimization - Adds a TypeScript tip with a plain JS note - Tightens prose throughout (net -20 lines) ## Test plan - [x] Review the rendered docs page for clarity and correctness - [x] Verify all internal links resolve (`basic-operations.md`, `configuration-options.md`, etc.) - [x] Confirm the Vue 3 StackBlitz demo link still works <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Documentation-only edits to integration guides; no application code, auth, or data paths change. > > **Overview** > Aligns the **Vue** and **React** integration guides around the same TypeScript-first docs pattern and makes **`markRaw`** the primary Vue integration story instead of a separate provider class. > > **Vue (`integration-with-vue.md`):** Drops the `SpreadsheetProvider` wrapper and `spreadsheet-provider.ts` example in favor of a single `<script setup>` sample that builds `HyperFormula` inside **`markRaw`**, keeps sheet output in a `ref`, and calls **`destroy()`** on unmount. Template buttons gain **disabled** states tied to whether results are shown. Copy explains that only `values` is reactive and points mutators at **Basic operations**. The **Nuxt/SSR** section is shortened: HyperFormula is fine on the server; **`<ClientOnly>`** is framed as an optional way to skip server work, not a requirement. Troubleshooting keeps the same `licenseKeyValidityState` error but uses consistent `markRaw` snippets (including `hf` naming). > > **React (`integration-with-react.md`):** Adds the same **::: tip TypeScript** block (remove annotations for plain JS) and removes the redundant standalone “drop type annotations” paragraph after the example. > > No runtime or API changes—docs and examples only. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 650eb19. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 0e047ee commit 79f2f15

2 files changed

Lines changed: 35 additions & 53 deletions

File tree

docs/guide/integration-with-react.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ The HyperFormula API is identical in a React app and in plain JavaScript. This g
44

55
Install with `npm install hyperformula`. For other options, see the [client-side installation](client-side-installation.md) section.
66

7+
::: tip TypeScript
8+
All examples use TypeScript. Remove the type annotations to use plain JavaScript.
9+
:::
10+
711
## Basic usage
812

913
Hold the HyperFormula instance in a `useRef` so it survives re-renders. Initialize it inside `useEffect` and release it in the cleanup function. Use `useState` to toggle between raw formulas and computed values.
@@ -67,8 +71,6 @@ export default function SpreadsheetComponent() {
6771
}
6872
```
6973

70-
If you use JavaScript instead of TypeScript, drop the type annotations — the rest of the pattern is unchanged.
71-
7274
## `React.StrictMode` double invocation
7375

7476
In development, React runs effects twice (mount → unmount → mount) to surface cleanup bugs. The pattern above is correct for StrictMode because `destroy()` runs before the re-mount creates a new instance, so no work leaks between the two lifecycles. Do not switch to a module-scoped singleton as a workaround — it will break StrictMode semantics.

docs/guide/integration-with-vue.md

Lines changed: 31 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,47 @@ The HyperFormula API is identical in a Vue 3 app and in plain JavaScript. This g
44

55
Install with `npm install hyperformula`. For other options, see the [client-side installation](client-side-installation.md) section.
66

7-
## Basic usage
8-
9-
Wrap the HyperFormula instance inside a plain class so it stays outside Vue's reactivity system (see [Troubleshooting](#vue-reactivity-issues) below for why this matters). Hold derived data in `ref` so the template updates when you reassign the ref's `.value`.
7+
::: tip TypeScript
8+
All examples use TypeScript. Remove the type annotations to use plain JavaScript.
9+
:::
1010

11-
```typescript
12-
// spreadsheet-provider.ts
13-
import { HyperFormula, type CellValue } from 'hyperformula';
14-
15-
export class SpreadsheetProvider {
16-
private hf: HyperFormula;
17-
18-
constructor(data: (string | number | null)[][]) {
19-
this.hf = HyperFormula.buildFromArray(data, {
20-
licenseKey: 'gpl-v3',
21-
// more configuration options go here
22-
});
23-
}
24-
25-
getCalculatedValues(): CellValue[][] {
26-
return this.hf.getSheetValues(0);
27-
}
28-
29-
getRawFormulas(): (string | number | null)[][] {
30-
return this.hf.getSheetSerialized(0) as (string | number | null)[][];
31-
}
32-
33-
destroy() {
34-
this.hf.destroy();
35-
}
36-
}
37-
```
11+
## Basic usage
3812

39-
Use the class from a component with `<script setup>`:
13+
Pass the HyperFormula instance through Vue's [`markRaw`](https://vuejs.org/api/reactivity-advanced.html#markraw) to opt it out of the reactivity system (see [Troubleshooting](#vue-reactivity-issues) below for why this matters). Hold derived data in `ref` so the template updates when you reassign the ref's `.value`.
4014

4115
```vue
4216
<script setup lang="ts">
43-
import { onUnmounted, ref } from 'vue';
44-
import type { CellValue } from 'hyperformula';
45-
import { SpreadsheetProvider } from './spreadsheet-provider';
46-
47-
const provider = new SpreadsheetProvider([
48-
[1, 2, '=A1+B1'],
49-
// your data rows go here
50-
]);
17+
import { markRaw, onUnmounted, ref } from "vue";
18+
import { HyperFormula, type CellValue } from "hyperformula";
19+
20+
const hf = markRaw(
21+
HyperFormula.buildFromArray(
22+
[
23+
[1, 2, "=A1+B1"],
24+
// your data rows go here
25+
],
26+
{ licenseKey: "gpl-v3" },
27+
),
28+
);
5129
5230
const values = ref<CellValue[][]>([]);
5331
5432
function runCalculations() {
55-
values.value = provider.getCalculatedValues();
33+
values.value = hf.getSheetValues(0);
5634
}
5735
5836
function reset() {
5937
values.value = [];
6038
}
6139
62-
onUnmounted(() => provider.destroy());
40+
onUnmounted(() => hf.destroy());
6341
</script>
6442
6543
<template>
66-
<button @click="runCalculations">Run calculations</button>
67-
<button @click="reset">Reset</button>
44+
<button @click="runCalculations" :disabled="!!values.length">
45+
Run calculations
46+
</button>
47+
<button @click="reset" :disabled="!values.length">Reset</button>
6848
<table v-if="values.length">
6949
<tr v-for="(row, r) in values" :key="r">
7050
<td v-for="(cell, c) in row" :key="c">{{ cell }}</td>
@@ -73,11 +53,11 @@ onUnmounted(() => provider.destroy());
7353
</template>
7454
```
7555

76-
The class keeps the HyperFormula instance as a private field, so Vue's reactivity Proxy never reaches it. This is the same pattern used in the [Vue 3 demo](#demo).
56+
`hf` is marked raw so Vue never proxies it — `values` is the only reactive piece. To mutate data, call any HyperFormula method (e.g. `setCellContents`) then reassign `values.value` to trigger a re-render. See [Basic operations](basic-operations.md) for the full mutation API.
7757

7858
## Server-side rendering (Nuxt)
7959

80-
The class above is already SSR-safe — HyperFormula has no browser-only API dependency. To skip the (otherwise wasted) server-side instantiation in Nuxt, wrap the component with `<ClientOnly>`.
60+
HyperFormula has no browser-only API dependency. To skip server-side computation, wrap the component with `<ClientOnly>`.
8161

8262
## Troubleshooting
8363

@@ -89,16 +69,16 @@ If you encounter an error like
8969
Uncaught TypeError: Cannot read properties of undefined (reading 'licenseKeyValidityState')
9070
```
9171

92-
it means that Vue's reactivity system tried to deeply observe the HyperFormula instance. Vue wraps reactive objects in a `Proxy` that intercepts every property access; when that proxy reaches a non-trivial instance with its own internal state, identity checks and lazy-initialized maps break. The fix is to opt the instance out of reactivity with Vue's [`markRaw`](https://vuejs.org/api/reactivity-advanced.html#markraw):
72+
it means that Vue's reactivity system tried to deeply observe the HyperFormula instance. Vue wraps reactive objects in a `Proxy` that intercepts every property access; when that proxy reaches a non-trivial instance with its own internal state, identity checks and lazy-initialized maps break. The fix is to opt the instance out of reactivity with [`markRaw`](https://vuejs.org/api/reactivity-advanced.html#markraw):
9373

9474
```typescript
95-
import { markRaw } from 'vue';
96-
import { HyperFormula } from 'hyperformula';
75+
import { markRaw } from "vue";
76+
import { HyperFormula } from "hyperformula";
9777

98-
const hfInstance = markRaw(
78+
const hf = markRaw(
9979
HyperFormula.buildEmpty({
100-
licenseKey: 'gpl-v3',
101-
})
80+
licenseKey: "gpl-v3",
81+
}),
10282
);
10383
```
10484

0 commit comments

Comments
 (0)