Git Product home page Git Product logo

mini-rx-store's Introduction

MiniRx - Logo

semantic-release MIT License Tests styled with prettier

MiniRx

Welcome to MiniRx, the reactive state management platform.

MiniRx Store

NPM Downloads

Framework-agnostic reactive state management library based on RxJS.

README

Docs site: mini-rx.io

MiniRx Signal Store

NPM Downloads

Signal-based reactive state management library for Angular.

README

License

MIT

Contributors โœจ

Thanks goes to these wonderful people (emoji key):

Pieter Van Poyer
Pieter Van Poyer

๐Ÿ’ป
Florian Spier
Florian Spier

๐Ÿ’ป ๐Ÿค”
Carsten
Carsten

๐ŸŽจ
Maximo Cudich-Sieburger
Maximo Cudich-Sieburger

๐Ÿ’ป
sashion
sashion

๐Ÿ’ป
BrainCrumbz
BrainCrumbz

๐Ÿ’ป
vangenechtenbert
vangenechtenbert

๐Ÿ‘€
mvmiert
mvmiert

๐Ÿ‘€
Karel De Smet
Karel De Smet

๐Ÿ’ป ๐Ÿ‘€

This project follows the all-contributors specification. Contributions of any kind welcome!

mini-rx-store's People

Contributors

allcontributors[bot] avatar carlos-ds avatar dependabot[bot] avatar giuseppepiscopo avatar m5150 avatar mini-rx avatar pietervanpoyer avatar portofantwerp avatar portofantwerpbruges avatar sashion avatar spierala 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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mini-rx-store's Issues

Edit Items which have been subscribed to

I'd like to have a possibility to edit an item before it gets passed into the

.select(state=> state.data['myItemId']).subscribe(myItem => {})

callback function after I selected it from the store. Is there a way to see what items are currently subscribed too? Or is there a way hook into 'subscribe' to edit 'myItem' I subscribed to before it get's passed into my subscribe callback?

Type parameter 'OT' has a circular constraint. createEffect typing broken in TS 5.4

image

Angular 17.3
TS 5.4.5

The same issue appears in NgRx/Effects: ngrx/platform#4275
createEffect in MiniRx is heavily inspired by NgRx...

We can apply the same fix as the NgRx team.

In the meanwhile it is possible to downgrade to TS 5.3.3 and the issue is gone.

This libs have to be fixed:

  • mini-rx-store
  • @mini-rx/common

There is a refactor of mini-rx-store ongoing (#209) to use also @mini-rx/common. The refactor of mini-rx-store will be released as a new major version (v6). I suggest to release the TypeScript fix also as part of v6. In that case the fix has to be applied only in @mini-rx/common.

`configureStore` was called multiple times

as you can see I create a Class and use library:

import {
  configureStore,
  Store as MiniStore,
  ReduxDevtoolsExtension,
  ImmutableStateExtension,
  UndoExtension,
  LoggerExtension,
} from "mini-rx-store";
import { take } from "rxjs";
import { STATES_INFO } from "./states-info";
import { SsrService } from "@/app/shared/services/ssr/ssr.service";
import COUNTER_EFFECTS from "./counter/counter.effect";
import { counterReducer } from "./counter/counter.reducer";
import { initialCounterState } from "./counter/initial-counter.state";
import superjson from 'superjson';
import * as devalue from 'devalue';

interface StoreOptions {
  reducers: any;
  initialState: any;
}

export class StateManager {
  private ssrService = SsrService.instance();
  private miniState: MiniStore = null!;
  private static mirror: StateManager;
  private static storeOptions: StoreOptions = {
    reducers: {
      counter: counterReducer,
    },
    initialState: {
      counter: initialCounterState,
    },    
  }
  private constructor() {}

  static instance(): StateManager {
    if (!StateManager.mirror) {
      StateManager.mirror = new StateManager();
      StateManager.mirror.init(this.storeOptions);
    }
    return StateManager.mirror;
  }

  private init(options: StoreOptions) {

    let extensions: any[] = [
      new ImmutableStateExtension(),
      new UndoExtension({ bufferSize: 100 }),
      // new LoggerExtension(),      
    ];
    if (this.ssrService.isPlatformBrowser()) {
      extensions.push(
        new ReduxDevtoolsExtension({
          name: "BAMAN SHOWCASE",
          maxAge: 100,
          latency: 0,
        }),        
      )
    }

    const opt = {
      ...options,
      extensions,
    };

    this.miniState = configureStore(opt);
    COUNTER_EFFECTS.forEach((effect: any) => this.miniState.effect(effect));
  }

  get store(): MiniStore {
    return this.miniState;
  }

  exportStates(): Promise<string> {
    return new Promise((resolve, reject) => {
      this.miniState
        .select((states: any) => states)
        .pipe(take(1))
        .subscribe({
          next: (obj) => { 
            // let sJson = superjson.stringify(obj);
            let sJson = devalue.stringify(obj);
            
            resolve(sJson);
          },
          error: (error) => {
            reject(error);
          },
        });
    });
  }

  resetStore() {
    STATES_INFO.forEach((state: any) =>
    this.miniState.dispatch(state.actions.reset())
    );
  }

  replaceStore(sJson: string) {
    let store = this.miniState;
    // let obj = superjson.parse<any>(sJson);
    let obj = devalue.parse(sJson);
    
    Object.keys(obj).forEach(function (key, index) {
      const state = STATES_INFO.find((s) => s.name === key);
      if (state) {
        store.dispatch(state.actions.replace(obj[key]));
      }
    });
  }
}

but sometimes, when I refresh prject, it says: @mini-rx: configureStore was called multiple times.
I expect each time class created, get single instance of store.
how can I solve it?

Refactor: mini-rx-store: use @mini-rx/common lib

Background

The MiniRx Signal Store uses the same concept as the original RxJS-based MiniRx Store. As a result there is a lot of code which can be shared between the two store solutions. For that reason the @mini-rx/common lib was created. @mini-rx/common is already used by the Signal Store.

See here the full public API of @mini-rx/common: https://github.com/spierala/mini-rx-store/blob/master/libs/common/src/index.ts

Task

  • Refactor MiniRx Store (mini-rx-store lib) to make use of the @mini-rx/common lib. Own custom implementations should be replaced with code from @mini-rx/common.
  • All public API of @mini-rx/common should be used by mini-rx-store (if not used directly, at least some code should be re-exported. See the Signal Store exporting some code from @mini-rx/common: https://github.com/spierala/mini-rx-store/blob/master/libs/signal-store/src/index.ts#L24-L42)
  • Signal Store should be used as orientation. In the end Signal Store and MiniRx Store should look quite identical. The main difference is that Signal Store is tailored to Angular Signals and MiniRx Store uses RxJS.

Subtasks

This is a quite large task. But is is definitely possible to create smaller sub-tasks:

Tests

mini-rx-signal store is covered by a lot of unit tests.
The tests will make the refactor relatively safe.

Indeed some tests can also be removed if mini-rx-store code is replaced with @mini-rx/common code.

Refactor to Nx Workspace

Currently MiniRx uses an Angular workspace (Angular 9) to manage the two libraries (mini-rx-store and mini-rx-store-ng) and the two demo apps.

Using a Nx workspace is the more modern approach and it includes many of the community best practices (Jest, EsLint, Cypress). Read more here: https://nx.dev/

With Nx we can create real TypeScript libraries: https://www.youtube.com/watch?v=-OmQ-PaSY5M&t=4s
That is exactly what we need for the mini-rx-store library.

Overview of what we need:
Libs:
mini-rx-store: typescript library (publishable), migrate Jest tests
mini-rx-store-ng: angular library (publishable), migrate Jest tests

Apps:
mini-rx-store-showcase (angular app)
mini-rx-store-showcase-redux (angular app)
or:
replace the two apps with this angular app: https://github.com/spierala/mini-rx-angular-demo

Read more about publishable libraries in Nx: https://nx.dev/l/r/structure/buildable-and-publishable-libraries#publishable-libraries

Migration:
I experimented with migrating existing Angular workspaces to Nx and the experience was not so great. IMO it is better to create an empty Nx workspace and create the libs and apps and then copy&paste the src code from the old Angular workspace.

Make dependency on window optional

I am using this package in a node environment (I need state management and I am using rxjs extensively so this project works quite neatly) and I am seeing window is not defined errors when running my application. I understand that you want to provide redux devtools compatibility which requires setting properties on the window. Could you perhaps check the presence of the window global variable and perform the redux devtools setup logic only if it is defined? If you are open to a pull request let me know.

Production usage

I have to start several new projects... Can I use Mini-RX-store in production and be sure to make the right choice?. Development seems to have been interrupted for a few months. Thanks in advance...

An argument for 'observableOrValue' was not provided.

When using effect that don't require an argument ( like loadTodos() is your documentation). Typescript complain about missing argument (observableOrValue: unknown) => void

feature-store.d.ts in line 15 =====> (observableOrValue: ObservableType | Observable) => void> the argument is not optional

Angular 15
"mini-rx-store": "^4.1.0",
"mini-rx-store-ng": "^3.0.0",
"typescript": "4.8.4",

store-devtools trace tab not working

Originally posted in the NgRx repo, but the same issue exists in MiniRx Store.

hi,
currently the trace tab displays the message: "To enable tracing action calls, you should set trace option to true for Redux DevTools enhancer. Refer to this page for more details"

i could not find any option like trace: boolean in the docs or typescript file. is there an option for tracing? if not will there be one in the future? im using angular v13.

thank you

See original issue in NgRx: ngrx/platform#3517

RFC: Add ComponentStore for managing local component state

A Component Store could be beneficial to manage state at a local (component) level.

But let's see what we have already in MiniRx...

MiniRx Store and Feature Store manage state at a global level:

MiniRx Store

  • Is a classic Redux Store which manages a global state object
  • In most cases the global state has the lifespan of the application
  • Redux with its Actions and Reducers allows us to use Redux Dev Tools
  • Store Extensions like the UndoExtension or ImmutableExtension are MetaReducers, which require Redux too

Read more in the MiniRx Store docs here: https://mini-rx.io/docs/redux

MiniRx Feature Stores

  • Feature Store state is also registered in the global state object
  • Feature Stores use Redux under the hood, so we can also use Redux Dev Tools and benefit from other Store Extensions (e.g. UndoExtension, ImmutableExtension)
  • Feature Stores allow to interact directly with a specific feature state with a more easy API (no Redux Boilerplate :))
  • API: select, setState, undo, effect, get state()
  • Feature Stores are destroyable, therefore they can take the role of a Component Store

Read more in the MiniRx Feature Store docs here: https://mini-rx.io/docs/fs-quick-start

Feature Stores as Component Store

MiniRx Feature Stores come close to a Component Store since they are destroyable.
See an example in the Angular Demo here: https://angular-demo.mini-rx.io/#/counter

The "counter" Feature Store looks like this:
https://github.com/spierala/mini-rx-angular-demo/blob/main/src/app/modules/counter/state/counter-state.service.ts

Note that it has to provide a unique feature key:

constructor() {
    super('counter-' + id++, initialState);
}

The feature key is used to register the counter states into the global state object:
Screenshot 2021-11-09 at 20 40 24

The counter component provides its own instance of the counter-state.service. See here:
https://github.com/spierala/mini-rx-angular-demo/blob/main/src/app/modules/counter/counter/counter.component.ts#L9

When the counter components are destroyed then the corresponding feature states are removed from the global state object.

Feature Stores as Component Store PROs & CONs

PROs

  • Existing functionality can be reused (the Feature Store class) to have a store with the lifespan of the component
  • The component state can be inspected with Redux Dev Tools, we can undo state changes, etc
  • Feature Stores automatically use the extensions which are provided for the global store (e.g. ImmutableExtension)
  • Since the component state is registered in the global state we can easily write selectors which combine other global state with the "component state"

CONs

  • It is necessary to provide a unique feature key when instantiating the Feature Store
  • When we register / unregister hundreds of Feature Stores at the same time there is a visible performance impact (only at the moment when the register/unregister happens, afterwards it is fine)
  • If there are a lot of Feature Stores used as Component Stores they might "pollute" the global state object with a lot of state which is meant as local state.
  • Local component state changes trigger emissions on the global state object which is odd and less performant.

MiniRx Component Store?

What could it look like?

  • Same API as Feature Stores (select, setState, effect, undo, get state())
  • No need to provide a unique feature key

Mini Redux?

  • The Undo functionality would require that the Component Store is a little Redux Store itself
  • If the Component Store is a little Mini Redux we could easily add support for some of the existing MiniRx extensions: ImmutableExtension, UndoExtension, LoggingExtension. Not sure about the Redux Dev Tools extension.

MiniRx Component Store PROs & CONs

PROs

  • Component Store would be more performant than Feature Store (mostly if you have hundreds of components using Component Store)

CONs

  • It could be confusing for the user of MiniRx to see another Store variant which is very similar to FeatureStore ("when should I use which one?"). We would have 3 variants: Store, Feature Store, Component Store
  • There will be a lot of duplication in the docs since most of the API is identical with FeatureStore
  • MiniRx would loose its focus on global state-management ( thats maybe a marketing thing ;) but I think a young library like MiniRx should have focus to become successful )
  • Thinking about the future: enitityAdapter for Redux and a EntityFeatureStore? Would we need then also a EntityComponentStore?

Make the best of Feature Store?

Internal unique id creation?
We could try to make the registration of multiple Feature Stores more straightforward, especially the unique id thing, which could be handled internally

Detached Mode?
Prevent Feature Store from the registration on the global Redux store. Instead the Feature Store could fall back to a local Redux.
Again we need Redux mostly to keep the undo functionality (using the UndoExtension).

Non Goals

  • Be a copy of ngrx/component-store

What do you think?

With SignalStore what 's the best way to get the raw state

When using some class store's functions (class implement FeatureStore), i need to get the RAW state from the signal FeatureStore. My implemented FeatureStore is injected in Angular constructor.
I tried to used the state() (this.store.state().XYZ) but the function is deprecated and still not replaced
Using this.store.select() return a signal. To get the RAW state i need to write this.store.select()() and it's very ugly.

What is the best practice to obtain the raw state from the store presently ?

Is there a way to access the root state in a reducer?

Is there any way to access the root state in a reducer? This would be to read data, not to manipulate the state beyond the reducer's scope.

In our use case, we need to look up values in a map, stored in a different branch of the root-state.

Entity management

It would be cool to make the management of entity data (collections of data: e.g. Products, Todos) more straight forward in MiniRx.
Currently the immutable updates of entities require quite some boilerplate code.

Existing solutions:

MiniRx Entity state:

MiniRx has two APIs to manage state... both need to be able to manage entities and if possible many entities in one feature state.

  • Redux API: NgRx entitiy adapter could be a good fit in general
  • FeatureStore, ComponentStore API: the entity adapter could be used there as well
  • Maybe a new thing: Entity Feature Store: extends Feature Store but sets up an entityAdapter internally. Here we would just manage one entity per Entity Feature Store

Challenge:

  • Investigate existing entity state management solutions
  • Add support for entity management in the Redux and the FeatureStore/ComponentStore API
  • Create EntityFeatureStore: manage (probably) just one entity but with a minimum of boilerplate

Notes

API

Regarding the naming of functions which update entity state: Most probably we should use similar/identical names like NgRx entityAdapter, Akita EntityStore or Elf. These names are known by developers.

New lib?

Should "MiniRx Entity" be part of the mini-rx-store lib or should it be a dedicated lib? Or is maybe a second entry point in mini-rx-store good enough (for tree-shaking)?

Is Redux Toolkit supported?

I discovered this really nice looking project and am thinking about switching to it for my state-management.
However, the documentation does not use Redux Toolkit, but simple Redux.

Is this library compatible with Redux Toolkit or will I have to write more boilerplate for simple Redux?

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.