Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/guide/integration-with-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ The HyperFormula API is identical in a React app and in plain JavaScript. This g

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

::: tip TypeScript
All examples use TypeScript. Remove the type annotations to use plain JavaScript.
:::

## Basic usage

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.
Expand Down Expand Up @@ -67,8 +71,6 @@ export default function SpreadsheetComponent() {
}
```

If you use JavaScript instead of TypeScript, drop the type annotations — the rest of the pattern is unchanged.

## `React.StrictMode` double invocation

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.
Expand Down
82 changes: 31 additions & 51 deletions docs/guide/integration-with-vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,47 @@ The HyperFormula API is identical in a Vue 3 app and in plain JavaScript. This g

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

## Basic usage

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`.
::: tip TypeScript
All examples use TypeScript. Remove the type annotations to use plain JavaScript.
:::

```typescript
// spreadsheet-provider.ts
import { HyperFormula, type CellValue } from 'hyperformula';

export class SpreadsheetProvider {
private hf: HyperFormula;

constructor(data: (string | number | null)[][]) {
this.hf = HyperFormula.buildFromArray(data, {
licenseKey: 'gpl-v3',
// more configuration options go here
});
}

getCalculatedValues(): CellValue[][] {
return this.hf.getSheetValues(0);
}

getRawFormulas(): (string | number | null)[][] {
return this.hf.getSheetSerialized(0) as (string | number | null)[][];
}

destroy() {
this.hf.destroy();
}
}
```
## Basic usage

Use the class from a component with `<script setup>`:
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`.

```vue
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import type { CellValue } from 'hyperformula';
import { SpreadsheetProvider } from './spreadsheet-provider';

const provider = new SpreadsheetProvider([
[1, 2, '=A1+B1'],
// your data rows go here
]);
import { markRaw, onUnmounted, ref } from "vue";
import { HyperFormula, type CellValue } from "hyperformula";

const hf = markRaw(
HyperFormula.buildFromArray(
[
[1, 2, "=A1+B1"],
// your data rows go here
],
{ licenseKey: "gpl-v3" },
),
);

const values = ref<CellValue[][]>([]);

function runCalculations() {
values.value = provider.getCalculatedValues();
values.value = hf.getSheetValues(0);
}

function reset() {
values.value = [];
}

onUnmounted(() => provider.destroy());
onUnmounted(() => hf.destroy());
</script>

<template>
<button @click="runCalculations">Run calculations</button>
<button @click="reset">Reset</button>
<button @click="runCalculations" :disabled="!!values.length">
Run calculations
</button>
<button @click="reset" :disabled="!values.length">Reset</button>
<table v-if="values.length">
<tr v-for="(row, r) in values" :key="r">
<td v-for="(cell, c) in row" :key="c">{{ cell }}</td>
Expand All @@ -73,11 +53,11 @@ onUnmounted(() => provider.destroy());
</template>
```

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).
`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.

## Server-side rendering (Nuxt)

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>`.
HyperFormula has no browser-only API dependency. To skip server-side computation, wrap the component with `<ClientOnly>`.

## Troubleshooting

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

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):
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):

```typescript
import { markRaw } from 'vue';
import { HyperFormula } from 'hyperformula';
import { markRaw } from "vue";
import { HyperFormula } from "hyperformula";

const hfInstance = markRaw(
const hf = markRaw(
HyperFormula.buildEmpty({
licenseKey: 'gpl-v3',
})
licenseKey: "gpl-v3",
}),
);
```

Expand Down
Loading