get-svelte
Version:
A simple, powerful state management library for Svelte applications, inspired by GetX for Flutter
328 lines (232 loc) • 8.05 kB
Markdown
# GetX for Svelte

A simple, powerful state management library for Svelte applications, inspired by [GetX for Flutter](https://pub.dev/packages/get).
[](https://badge.fury.io/js/get-svelte)
[](https://opensource.org/licenses/ISC)
## Features
- 🚀 Simple and intuitive API
- 🎯 Dependency injection for controllers
- 🔄 Reactive state management
- 🧩 Automatic component updates
- ♻️ Optional automatic controller disposal
- 🏷️ Tag-based controller identification
## Installation
```bash
# npm
npm install get-svelte
# pnpm
pnpm add get-svelte
# yarn
yarn add get-svelte
```
## Basic Usage
### 1. Create a Controller
First, create a controller that extends `GetxController`:
```typescript
import { GetxController } from 'get-svelte';
class CounterController extends GetxController {
count = 0;
increment() {
this.count++;
this.notifyListener(); // Notify listeners about state change
}
decrement() {
this.count--;
this.notifyListener(); // Notify listeners about state change
}
// Optional: Override onInit for initialization logic
protected onInit(): void {
console.log('CounterController initialized');
}
}
```
### 2. Register Controller
Register your controller with the `Get` service:
```typescript
import { Get } from 'get-svelte';
import { CounterController } from './CounterController';
// Register controller without tag
const counterController = Get.put(new CounterController());
// Or register with a tag for specific identification
const taggedController = Get.put(new CounterController(), { tag: 'main-counter' });
```
### 3. Use Controller in Components
Use the `GetListener` component to automatically react to controller state changes:
```svelte
<script>
import { Get, GetListener } from 'get-svelte';
import { CounterController } from './CounterController';
// Find existing controller or throw error if not found
const counterController = Get.find(CounterController);
// Or find controller by tag
const taggedController = Get.find(CounterController, { tag: 'main-counter' });
</script>
<GetListener controller={counterController}>
{#snippet builder(controller)}
<div>
<h1>Counter: {controller.count}</h1>
<button on:click={() => controller.increment()}>Increment</button>
<button on:click={() => controller.decrement()}>Decrement</button>
</div>
{/snippet}
</GetListener>
```
## API Reference
### `Get` Class
The main service class for controller management:
#### `Get.put<T extends GetxController>(controller: T, params?: { tag?: string }): T`
Registers a controller with the Get system. If a controller of the same type and tag exists, returns the existing instance.
```typescript
const controller = Get.put(new MyController());
const taggedController = Get.put(new MyController(), { tag: 'unique-tag' });
```
#### `Get.find<T extends GetxController>(ControllerClass: new (...args: any[]) => T, params?: { tag?: string }): T`
Finds and returns a registered controller. Throws an error if not found.
```typescript
const controller = Get.find(MyController);
const taggedController = Get.find(MyController, { tag: 'unique-tag' });
```
#### `Get.isRegistered<T extends GetxController>(ControllerClass: new (...args: any[]) => T, params?: { tag?: string }): boolean`
Checks if a controller is registered with the Get system.
```typescript
const exists = Get.isRegistered(MyController, { tag: 'unique-tag' });
```
### `GetxController` Class
Base class for all controllers:
#### `notifyListener(): void`
Notifies all registered listeners about state changes. Call this method whenever your controller's state changes.
#### `onInit(): void`
Lifecycle hook called when the controller is initialized. Override this method to perform initialization tasks.
#### `dispose(): void`
Removes the controller from the Get system.
### `GetListener` Component
A Svelte component that listens to controller state changes and rebuilds its UI:
```svelte
<GetListener
controller={myController}
autoDestroy={true}>
{#snippet builder(controller)}
<!-- Your UI that uses controller state -->
<div>Count: {controller.count}</div>
{/snippet}
</GetListener>
```
#### Props:
- `controller`: The controller instance to listen to
- `autoDestroy` (optional, default: `true`): Whether to automatically dispose the controller when the component is destroyed
## Server-Side Rendering (SSR)
When using get-svelte in SSR environments, you need to prevent cross-request controller contamination.
### Quick Fix
Add this one line to your server request handler:
```typescript
// SvelteKit: src/hooks.server.ts
import { Get } from 'get-svelte';
export async function handle({ event, resolve }) {
Get.clearForSSR(); // Clear controllers from previous requests
return await resolve(event);
}
```
```typescript
// Express.js
app.use((req, res, next) => {
Get.clearForSSR();
next();
});
```
```typescript
// Custom Node.js server
function handleRequest(req, res) {
Get.clearForSSR();
// ... your request handling
}
```
### Why This is Needed
In SSR environments, the static controller registry is shared across all requests. Without clearing:
- **❌ User A's data might briefly appear to User B**
- **❌ Memory leaks from accumulated controllers**
- **❌ Cross-request state contamination**
With `Get.clearForSSR()`:
- **✅ Each request starts with a clean slate**
- **✅ Complete isolation between users**
- **✅ No memory leaks**
- **✅ Controllers properly disposed**
### Additional Methods
```typescript
// Manual cleanup (same as clearForSSR)
Get.deleteAll();
// Check if controllers exist
const hasControllers = Get.isRegistered(MyController);
```
## Examples
### Counter Example
```typescript
// CounterController.ts
import { GetxController } from 'get-svelte';
export class CounterController extends GetxController {
count = 0;
increment() {
this.count++;
this.notifyListener();
}
decrement() {
this.count--;
this.notifyListener();
}
}
```
```svelte
<!-- Counter.svelte -->
<script>
import { Get, GetListener } from 'get-svelte';
import { CounterController } from './CounterController';
// Create and register controller if not already registered
const controller = Get.isRegistered(CounterController)
? Get.find(CounterController)
: Get.put(new CounterController());
</script>
<GetListener controller={controller}>
{#snippet builder(ctrl)}
<div>
<h2>Count: {ctrl.count}</h2>
<button on:click={() => ctrl.increment()}>+</button>
<button on:click={() => ctrl.decrement()}>-</button>
</div>
{/snippet}
</GetListener>
```
## Advanced Usage
### Using Tags for Multiple Instances
```typescript
// Create multiple counter instances
const counter1 = Get.put(new CounterController(), { tag: 'counter1' });
const counter2 = Get.put(new CounterController(), { tag: 'counter2' });
// Later, retrieve specific counters
const counter1Instance = Get.find(CounterController, { tag: 'counter1' });
const counter2Instance = Get.find(CounterController, { tag: 'counter2' });
```
### Manual Controller Lifecycle Management
```svelte
<script>
import { Get, GetListener } from 'get-svelte';
import { MyController } from './MyController';
const controller = Get.put(new MyController());
function cleanupController() {
controller.dispose();
}
</script>
<GetListener
controller={controller}
autoDestroy={false}>
{#snippet builder(ctrl)}
<!-- Your UI components here -->
<div>Data: {ctrl.data}</div>
{/snippet}
</GetListener>
<button on:click={cleanupController}>Dispose Controller</button>
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
ISC License
## Author
Created by [Abdalmonem](https://inkedoo.com)