Store: A Clear & Easy Way to Clear/Reset States is Needed

Created on 11 Feb 2019  ·  23Comments  ·  Source: ngxs/store

[UPDATE] The plugin is published here: https://www.npmjs.com/package/ngxs-reset-plugin

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

Current behavior

When a developer wants to clear global state upon various events (e.g. logout);

  • a meta reducer looks like the only way available.
  • the required work and know-how increases a lot if there is also a requirement to keep some states while deleting others.

When a developer wants to reset some states to their default values upon various events (e.g. route change);

  • again, a meta reducer can help, but is slightly complicated to implement.
  • it is quite common to add an action per state to set the defaults back, but this costs time and causes a lot of boilerplate.
  • the required work and know-how increases a lot if there is also a requirement to reset several states at once.

Expected behavior

A clear and easy to implement method for clearing and resetting state to be present, either as a core feature or as a plugin, free from breaking changes if possible.

What is the motivation / use case for changing the behavior?

Improved development experience, decreased development time/cost

Other

There is an open PR regarding this issue

plugins

Most helpful comment

I'm not sure that this should be added to the main repository, I can give access to the development of the plugin in our laboratory: https://github.com/ngxs-labs

Hi @splincode

I don't wish to look persistent, and believe me I won't keep it for long, but I also don't understand why such a plugin belongs to Labs when:

  • it corresponds to a very basic need, i.e. clearing and resetting state.
  • it does not introduce any breaking changes.
  • it does not touch the codebase of the store or any other library.
  • it is not in development phase, although I am sure it will become better with your contribution in time.
  • it is fully tested and documented.
  • it follows your guidelines here and here.

Nevertheless, if you are 100% sure it doesn't belong here, you can decline my PR. I'll respect your decision.

All 23 comments

Hello,

I think that kind of plugin would be helpful. Thank you for that feature.

It would be really nice if you could provide this feature. And it would also save a great deal of time for developers like me :)

I'm not sure that this should be added to the main repository, I can give access to the development of the plugin in our laboratory: https://github.com/ngxs-labs

I'm not sure that this should be added to the main repository, I can give access to the development of the plugin in our laboratory: https://github.com/ngxs-labs

Hi @splincode

I don't wish to look persistent, and believe me I won't keep it for long, but I also don't understand why such a plugin belongs to Labs when:

  • it corresponds to a very basic need, i.e. clearing and resetting state.
  • it does not introduce any breaking changes.
  • it does not touch the codebase of the store or any other library.
  • it is not in development phase, although I am sure it will become better with your contribution in time.
  • it is fully tested and documented.
  • it follows your guidelines here and here.

Nevertheless, if you are 100% sure it doesn't belong here, you can decline my PR. I'll respect your decision.

Hello @splincode

please check this post; https://stackoverflow.com/questions/50753025/resetting-state-in-ngxs

Our team also faced with the same issue. Its kind of common problem for ngxs.

This is why It should be in main repo.

Best,

@armanozak I agree with @splincode. It should belong to the Labs repository, I've read your issue multiple times and cannot understand what problem it's solving :disappointed:

is it better than the current solution?
https://ngxs.gitbook.io/ngxs/concepts/store#reset

I need more examples for proof

is it better than the current solution?
https://ngxs.gitbook.io/ngxs/concepts/store#reset

Yes it is.

Direct store reset, i.e. store.reset(myNewStateObject), can:

  • replace a state with a new one.
  • do it silently, which means the replacement is untrackable by Redux DevTools or the Logger plugin.

Drawbacks/missing features of current solution are:

  • the developer needs to select states or state snapshots and build a new state tree up of them. This is both difficult and painful multi-level states are in place. It becomes even more difficult when multiple state slices are required to be kept. Imagine shared parent states and merging several state slices based on path meta data.
  • there is no method proposed to reset a state to its already defined defaults. That is an even more crucial requirement due to the simple fact that it is more frequently needed and replacing a state with anything other than defaults can break your app.
  • the execution is untrackable by Redux DevTools or the Logger plugin. What if I want to track it? Moreover, what if I need to listen to state resets and act upon it declaratively (such as a handler)?
  • has a very meaningful warning below, and I quote: "Warning: Using this can cause unintended side effects if inproperly used and should be used with caution!"

How reset plugin improves all that is:

  • by resolving default values of state classes, it decreases the amount of work and boilerplate when a reset-to-defaults is required.
  • by providing an easy to implement API (simple action dispatch) it resolves multi-level state structure via path meta data and keeps/resets the states as intended, free from all the complexity.
  • by doing all this following the CQRS pattern (as intended by NGXS in the first place) it supports every tracking method any other action dispatch provides, including but not limited to Redux DevTools, Logger plugin, and Actions stream.

How the concept of meta reducers related to all this is:

  • every issue above can be solved using them. However, that literally means writing your own plugin.
  • the plugin IS basically a meta reducer, but a robust and carefully tested one.

Is this information helpful?

@armanozak I don’t understand why you don’t want to add this as a development in https://github.com/ngxs-labs. Because the approach proposed by you cannot fully comply with my ideas on how it should be better. We also release the experimental features exactly there:

https://github.com/ngxs-labs/entity-state
https://github.com/ngxs-labs/emitter

If it is the community that is important to you, and not just add this pull request, then I suggest giving you access to our laboratory and creating your project there.

If your project is really popular and useful and passes the test of time, you can add it to the main repository.

I am sorry @splincode but you lost me at “the approach proposed by you cannot fully comply with my ideas“ part. Please decline my PR. I will find another way to help people.

@markwhitfeld What do you think?

@mehmetakifalp what difference in what repository is it? if you can install this package just this way

npm i @ngxs-labs/reset-plugin

@armanozak let's think about how we can improve your idea, the problem is that it seems to me that this should not be a plugin

you could modify the built-in reset method

this.store.reset({ ... }); // Dispatch with payload

this.store.reset(); // reset all states by defaults

this.store.reset(MyState) // reset state by class

@splincode I am open to any ideas and am willing to revise any part based on feedback. Let's take what you proposed above as an initial idea and discuss possible outcomes. Even if we agree to disagree in the end, I still think this will present the perfect opportunity to improve the way we think.

First of all, I suppose I understand your reasoning. This is quite in parallel to where I started thinking about this issue about a month ago. I thought "This is a fundamental feature, so it'd better be implemented in the store as a built-in function, probably extending the capability of store.reset.". The initial API I came out with was very similar to what you are proposing with an addition:

this.store.reset([ MyState, MyOtherState ]);

I cloned the repo and started inspecting the code. There it was, at the bottom of store.ts file, quite a straightforward setState behind the scene. I was going to go for it, but then I thought, "Argh, this will just solve half of the problems.". The questions I had in mind are:

  • Is it not confusing to use the same method for both deleting state and resetting them to their defaults? Wouldn't it be better if we had separate API for these two?
  • How about the tracking issue? It is important to keep this behavior in order to avoid breaking changes and for some use cases, but again, it is terrible for what I am aiming for.
  • It is not going to be opt-in. Can we not find another way to achieve the same results?
  • Is it still not against CQRS pattern?

Well, now you know why I went for a plugin. What do you think?

@armanozak I can definitely see the use case and usefulness of this plugin. It looks like this is an issue that you have fought with and come up with a decent approach.
I agree with the rest of the team that this should become a labs project for now though.
The motivations for this are:

  • The main library and plugins in the main repo are maintained by the core team and are locked into the same version increments as the store lib. This does not do well for something that may need to iterate a few times to get a final approach fleshed out
  • Because of the large adoption of the framework we need to be careful to only bring in mature plugins and ideas as to avoid the possibility of breaking changes later.
  • The labs organisation is designed for quick iteration and for plugin authors to get some momentum and feedback on tier ideas in a repository that they have full rights to (without the pressure of having to get it right the first time).
  • Another aspect of our approach at the moment is to look for ways to create extensibility points in the main lib so that plugins are free to create their own variants of library behaviour.

Thanks for your passion for making this useful functionality available to others. We appreciate all ideas and contributions

@armanozak The problem is that we need to implement ngxsOnChanges method. Accordingly, we need to track changes at the store

https://github.com/ngxs/store/issues/749

I. We can add new action ResetAction and create new pull request
https://github.com/ngxs/store/blob/master/packages/store/src/store.ts#L92

/**
   * Reset the state to a specific point in time. This method is useful
   * for plugin's who need to modify the state directly or unit testing.
   */
  reset(state: any) {
    this.dispatch(new ResetAction(state)); // tracking
    return this._internalStateOperations.getRootStateOperations().setState(state);
  }

II. So for compatibility we can add new method clear

this.store.clear(); // reset all states by defaults
this.store.clear(MyState) // reset state by class
this.store.clear(MyState, {  ... }) // reset state by value and state name
this.store.clear([ MyState, MyOtherState ]) // reset states
this.store.clear([ MyState, MyOtherState ], [ { .. }, { .. } ]) // reset states

@splincode I would recommend the following API for the clear method:

import { StateClass } from './internal/internals';

type SingleOrListOf<T> = T | T[];

interface StateClear {
  state: StateClass;
  value?: any;
}

type StateClearConfig = SingleOrListOf<StateClass | StateClear>;


export class Store {
  // rest of the store

  private _clear(item: StateClass | StateClear) {
    // clear per state here
  }

  clear(config?: StateClearConfig) {
    if (!config) {
      // clear all states here
    }
    else if (Array.isArray(config)) {
      config.forEach(item => this._clear(item));
    }
    else {
      this._clear(config);
    }
  }
}

The method then will clear:

this.store.clear();   // all states to their defaults

this.store.clear(MyState);   // a specific state to its default

this.store.clear({
  state: MyState,
  value: { .. },
});   // state to given value

this.store.clear([ MyState, MyOtherState ]);   // multiple states to their defaults

this.store.clear([
  {
    state: MyState,
    value: { .. },
  }, {
    state: MyOtherState,
    value: { .. },
  },
]);   // multiple states to given values

I recommend this, because then you can do:

this.store.clear([
  MyState,
  {
    state: MyOtherState,
    value: { .. },
  },
]);   // some states to defaults, others to given values

and where will you dispatch?

I wouldn't worry about that at the moment. Actually, it might be a good idea to ask for community opinion about how to approach that dispatch. Should we dispatch once? Should we dispatch one action per state? Should we dispatch before clearing state? After maybe? Or both with different actions? Yeah, I think it would be a good idea to get community opinion before implementing the clear method.

@splincode I still think the clear method is _not_ a good idea.

  • It does not comply with the single responsibility principle.
  • The usability will be terrible when only a single state is wished to be kept and the rest is to be cleared.
  • I cannot see how dispatching an action will work with devtools and logger plugins.

By the way, I closed the PR, so I am not trying to say the plugin was a better idea. Nonetheless, I also think it was a better idea, so I will publish it from somewhere else.

You asked me a few times why I don't want reset plugin to be a part of Labs. I have seen, tried, loved, and starred all 3 available projects it the labs. Yet I don't dare use them. Why? Because they are in the "Labs". I personally don't think they are immature or experimental. However, being a part of an enterprise project, I know I cannot explain an architectural decision of using a "Labs" project to my superiors, if something goes wrong due to the use of Immer adapter for instance. So, I don't use it. This is exactly why I don't want the reset plugin to be a part of it. Sorry.

Keep up the good work and I will try to support you somehow else.

To anyone looking for this plugin,

You can find it here: https://www.npmjs.com/package/ngxs-reset-plugin

Was this page helpful?
0 / 5 - 0 ratings