Giter VIP home page Giter VIP logo

ngx-zustand's Introduction

NgxZustand

The Zustand adapter for angular.

Installation

with npm:

npm install ngx-zustand zustand

with yarn:

yarn add ngx-zustand zustand

First create a store

Create a service that extends ZustandBaseService.

interface CounterState {
  counter: number;
  increment: () => void;
  decrement: () => void;
}

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return (set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    });
  }
}

Use the service in your components

@Component({
  selector: 'app-counter-page',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div *ngIf="store$ | async as store">
      <div>
        count: {{ store.counter }}
        <div>
          <div><button (click)="store.increment()">+</button></div>
          <div><button (click)="store.decrement()">-</button></div>
        </div>
      </div>
    </div>
  `,
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CounterPageComponent {
  private counterService = inject(CounterService);
  store$ = this.counterService.useStore();
}

Recipes

Fetching Everything

store$ = this.store.useStore();

Selecting multiple state slices

foo$ = this.store.useStore((state) => state.foo);
bar$ = this.store.useStore((state) => state.bar);
fooAndBar$ = this.store.useStore((state) => ({
  foo: state.foo,
  bar: state.bar,
}));

Async actions

Just call set when you're ready, zustand doesn't care if your actions are async or not.

export class TodosStore extends ZustandBaseService<TodosState> {
  initStore() {
    return (set) => ({
      todos: [],
      loadTodos: () => {
        this.http.get<Todo[]>().subscribe((todos) => set({ todos }));
      },
    });
  }
}

Read from state in actions

set allows fn-updates set(state => result), but you still have access to state outside of it through get.

export class TodosStore extends ZustandBaseService<TodosState> {
  initStore() {
    return (set, get) => ({
      todos: [],
      action: () => {
        const todos = get().todos;
      },
    });
  }
}

Redux devtools middleware

You can override createStore function in order to include the middlewares you need.

import { devtools } from 'zustand/middleware';

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return devtools<CounterState>((set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    }));
  }
}

Persist middleware

import { createJSONStorage, persist } from 'zustand/middleware';

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore(): StateCreator<CounterState> {
    return (set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    });
  }

  override createStore() {
    return createStore(
      persist<CounterState>(this.initStore(), {
        name: 'counterStore',
        storage: createJSONStorage(() => sessionStorage),
      })
    );
  }
}

Middleware

You can functionally compose your store any way you like. Please check typescript guide to a better explanation of how to type middlewares.

// Log every time state is changed
const logMiddleware = (config) => (set, get, api) =>
  config(
    (...args) => {
      console.log('  applying', args);
      set(...args);
      console.log('  new state', get());
    },
    get,
    api
  );

export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return logMiddleware((set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    }));
  }
}

ngx-zustand's People

Contributors

joaopaulolousada avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

ngx-zustand's Issues

Expose vanilla store, or consume vanilla store

According to the code example, you have to create the store inside the Service.

Is it possible to expose the store created this way as a vanilla store. Or else allow me to create a vanilla store first outside of Angular, then import it into the Angular Service.

Example:

import { createStore } from 'zustand/vanilla'

const store = createStore((set) => ({
  counter: 0,
  increment: () => set((state) => ({ counter: state.counter + 1 })),
  decrement: () => set((state) => ({ counter: state.counter - 1 })),
});)

interface CounterState {
  counter: number;
  increment: () => void;
  decrement: () => void;
}

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return store
  }
}

I am fairly new to Angular, so I do appreciate if you could point me to a better way. My intention is to use Zustand (the vanilla store specifically) to interface between Angular app with a separate non-angular system.

The React adapter allows this via the create function

import { create } from 'zustand'
import { vanillaStore } from './vanillaStore'

const useStore = create(vanillaStore)

From Where I need to import the ZustandBaseService

import { Injectable } from '@angular/core';


interface CounterState {
  counter: number;
  increment: () => void;
  // decrement: () => void;
}

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return set => ({
      counter: 0,
      increment: counter => set({ counter }),
      // decrement: () => set(state => ({ counter: state.counter - 1 })),
    });
  }
}

I did something like that , but it's throwing an error like Cannot find name 'ZustandBaseService'.ts(2304) so from where I can import it??

have errors of ts witch library

I don't understand why these errors appear with the library, can someone help me. thank you

Information:
"typescript": "~4.0.2"
Angular CLI: 11.0.7
Node: 14.19.1
OS: linux x64

the errors:

Build at: 2023-11-09T12:49:24.967Z - Hash: f7bcd2d370c48f38868f - Time: 7962ms

Error: node_modules/ngx-zustand/lib/zustand-base.service.d.ts:13:53 - error TS1005: '>' expected.

13 useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
~
node_modules/ngx-zustand/lib/zustand-base.service.d.ts:13:58 - error TS1005: ',' expected.

13 useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
~
node_modules/ngx-zustand/lib/zustand-base.service.d.ts:13:63 - error TS1005: ';' expected.

13 useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
~
node_modules/ngx-zustand/lib/zustand-base.service.d.ts:13:64 - error TS1128: Declaration or statement expected.

13 useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
~
node_modules/ngx-zustand/lib/zustand-base.service.d.ts:13:79 - error TS1005: '(' expected.

13 useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
~
node_modules/ngx-zustand/lib/zustand-base.service.d.ts:14:1 - error TS1128: Declaration or statement expected.

14 }
~

File witch erros:
import { Observable } from 'rxjs';
import { StateCreator, StoreApi } from 'zustand/vanilla';
export { StateCreator } from 'zustand/vanilla';
import { useStore } from './use-store';
export declare abstract class ZustandBaseService {
private store;
constructor();
abstract initStore(): StateCreator;
createStore(): StoreApi;
getState(): T;
setState(payload: Partial): void;
useStore(): Observable;
useStore(selector: Parameters<typeof useStore<T, S>>[1]): Observable;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.