Skip to content

Feature: Support full and partial resets via reset() in withReset() #204

@rainerhahnekamp

Description

@rainerhahnekamp

Many apps need to re-initialize only parts of a store (e.g., clear a callState or selected slices) rather than resetting everything. Since state changes are mutations, they should be expressed as updater functions passed to patchState so multiple updates can run atomically and the Signal Store remains the only state mutation point.

Today, resetState is an instance method that can be called anywhere and may collide with other feature methods; it also doesn’t support partial resets.

This proposal introduces a type-safe reset() function - usable only inside patchState - for full and partial resets, as discussed here: PR #160 review comment.

Problem

  • withReset() currently resets the entire store; projects often need to reset only selected slices.
  • Mutations should be modeled as updater functions passed to patchState
  • resetState (instance method) can collide with other features

Proposal

Introduce a reset() function that returns an updater function for patchState and allows full and partial resets with strong typing (no string paths). Deprecate resetState.

// Full reset
function reset<TState>(): (state: TState) => TState;

// Partial reset (selected top-level slices)
function reset<TState, K extends keyof TState>(
  pick: (initial: TState) => Pick<TState, K>
): (state: TState) => TState;

Example

// Full reset
patchState(store, reset());

// Partial reset of top-level slices
patchState(store, reset(initial => ({ address: initial.address, user: initial.user })));
  • Partial resets copy selected slices from the stored initial snapshot.
  • Deep string paths like 'address.city' are intentionally not supported to keep the API simple and type-safe. Compose updates or add targeted helpers for nested cases.

Acceptance Criteria

  • reset() works with patchState for full and partial resets.
  • Type-safe selection of slices (no string keys/paths).
  • Backward compatibility: existing code continues to work.
  • Tests cover:
    • Full reset
    • Partial reset for one and multiple keys
    • Nested objects where only selected top-level keys are reset
    • Immutability preserved and atomicity via patchState
  • Documentation clearly explains the new API, usage, and deprecations.

Implementation Details

  • Add reset() to withReset in libs/ngrx-toolkit/src/lib/with-reset.ts.
    • Use the stored initial/reset snapshot (e.g., store._resetState) to compute next state.
    • Provide overloads for full and partial reset as above.
  • Update docs in docs/docs/with-reset.md.
  • Add tests in libs/ngrx-toolkit/src/lib/with-reset.spec.ts.

Deprecation Plan

  • Deprecate resetState (instance method) in types and docs; keep runtime behavior for now.
  • Consider also deprecating setResetState in favor of a single reset() flow (see questions).

Tasks

  • Implement reset() (full + partial) in libs/ngrx-toolkit/src/lib/with-reset.ts
  • Add unit tests in libs/ngrx-toolkit/src/lib/with-reset.spec.ts
  • Update docs: docs/docs/with-reset.md with API and examples
  • Update demo(s) to showcase reset() via patchState
  • Deprecate resetState

References

Further Questions

  1. Should we also deprecate setResetState to avoid two overlapping reset concepts and steer users to patchState(reset(...))?
  2. We currently use pseudo-internal members like _resetState and __setResetState__. Should we replace them with unique Symbols to hide internals from users?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions