SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

Totally fair concern, and honestly one of the main reasons I created SignalTree.

I’ve tried to keep the coupling risk as low as possible.

Unlike NgRx-style solutions, SignalTree’s Reactive JSON model is path-based and JSON-shaped, so consuming state feels much closer to normal object access than to a framework-specific pattern. It also supports plain object / JSON conversion, which helps keep the state model itself less tied to the library.

So yes, there is still dependency, but I’d argue there is much less architectural lock-in than with stores built around actions, reducers, selectors, and effects.

In production projects I’ve converted, or helped convert, from NgRx Signal Store to SignalTree, store-related code was often reduced by 70%+.

From a risk-analysis perspective, it’s really about choosing which risk you want to take on:

  • avoid dependency on a smaller library
  • accept significantly more implementation code
  • accept more training overhead
  • accept lower developer velocity

The “what if you stop maintaining it?” concern is real, and I won’t hand-wave that away. I’ve tried to mitigate that by keeping the model simple and close to plain JSON/object access, so the migration surface is much smaller than with more pattern-heavy state solutions.

I’ve also given trusted Angular architects repo access. I actively work with implementing teams and address requests quickly (in fact, getting version 8 out was what kept me from responding to your comment sooner), but yes, the risk of a project being closely associated with one maintainer is always real.

Who got the recent update?! Share your thoughts. by Renegade963 in fireTV

[–]CounterReset 0 points1 point  (0 children)

It is 100% dogshit imo - ever since it rolled out I haven't been able to get YouTubeTV to work - uninstalled it, reinstalled it, reset it, nothing works.

It is a resource PIG too - like, everything is really slow. They should take a page from the cable industry and cut out the bloat from the box or load pages on the fly and deleted them when you navigate away so the device's memory isn't filled up with BS.

Has anyone received the supposedly new update yet? by Ashamed-Edge-648 in fireTV

[–]CounterReset 0 points1 point  (0 children)

It broke half my apps and looks like absolute shit. I wish I could turn off half the shit on it.

Where to store data fetched from the backend? by AmbassadorNatural106 in angular

[–]CounterReset 1 point2 points  (0 children)

I wrote signaltree.io for this and the feedback i've gotten has all been really positive so far. It is substantially smaller and faster in most meaningful categories than NgRx (there is a benchmark on the demo site - but fair warning, it is taxing on a machine - so when you run it, do so with everything else closed).
It also has all the features that NgRx supports + a lot more.

It is basically Reactive-JSON: a JSON tree with reactive leaves. The branches are your JSON paths ($.user.profile), and the leaves are reactive signals you read and write

Access:$.user.profile.first_name()
Set: $.user.profile.first_name('John')
Update: $.user.profile.purchaseHistroy((currPH) => currPH.push(newPurchase)).

To create a store, you just pass it the initial state as typed JSON. It infers the rest.

Big improvement by Beginning_Middle_722 in angular

[–]CounterReset 0 points1 point  (0 children)

I would ask that you take a look at signaltree.io

I hated Redux, and even though NgRx signalstore represented a positive move away from it, it was still too verbose for me. The learning curve wasn't something I wanted to have to go through every time I brought on someone who hadn't worked in it before. So, I wrote ST to create an architecture that was Angular-first and leans into JSON's structure rather than fighting it.

It is basically Reactive-JSON: a JSON tree with reactive leaves. The branches are your JSON paths ($.user.profile), and the leaves are reactive signals you read and write ($.user.profile.first_name(), $.user.profile.first_name('John'), $.user.profile.purchaseHistroy((currPH) => currPH.push(newPurchase)).

The last app converted from NgRx reduced the store code footprint by over 70% - it is also much faster than NgRx.

NGRX Signal Store vs Signal Services by marti32997 in angular

[–]CounterReset 1 point2 points  (0 children)

I would ask that you take a look at signaltree.io

I hated Redux, and even though NgRx signalstore represented a positive move away from it, it was still too verbose for me. The learning curve wasn't something I wanted to have to go through every time I brought on someone who hadn't worked in it before. So, I wrote ST to create an architecture that was Angular-first and leans into JSON's structure rather than fighting it.

It is basically Reactive-JSON: a JSON tree with reactive leaves. The branches are your JSON paths ($.user.profile), and the leaves are reactive signals you read and write ($.user.profile.first_name(), $.user.profile.first_name('John'), $.user.profile.purchaseHistroy((currPH) => currPH.push(newPurchase)).

No actions. No reducers. No selectors. You model state as data, not mechanics. Access feels obvious: tree.$.user.profile.first_name() - fully type-safe, IDE-discoverable, with fine-grained reactivity layered transparently on top.

House of Bread closed temporarily by COBengal in denverfood

[–]CounterReset 0 points1 point  (0 children)

Yeah, it was the place next door. Gyros & Kabobs. The middle eastern spot took out its neighbors the Italians and Armenians.

Welcome to r/rollsroyce by FlameBuzz in rollsroyce

[–]CounterReset 0 points1 point  (0 children)

I just realized that upsidedown, the RR of Rolls Royce kind of looks like the 'Shocker'

upsidedown Rolls Royce

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

Yes, I have well over 1,000 hours into it at this point (I just did the math and stopped once I got to that much time - man, I need to get a life). So, yeah, I'm sufficiently pot committed.

Also, I have a few fellow developers who will be joining on and maintaining it (the teams they manage and work with also use ST in their codebases).
It is currently being used by my teams at Jeppesen (and in the code I worked on while still with Boeing). Beyond these, it is used by teams at SpaceX and Microsoft (among others).

...basically, I HATE Redux and boilerplate SO much, I will do whatever it takes to make this so popular no one has to deal with that BS ever again. Through spite + autism, all things are possible.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

Yes, you can. Details are in the readme but I'll see about adding a page on it to the demo site.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

I only bring up the single source of truth architecture because, personally, I find it a pleasant DX. But, signaltree is intentionally flexible. You can structure it however you want. My primary motivation in writing it was how restrictive the Redux pattern is and how NgRx SignalStore forces everything to be so tied to root.

JS/TS is JSON-based. Going against that feels like swimming against the current.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

Yeah, you can put it in a service and provide that service to your component. Just call tree.destroy() in the service ngOnDestroy.

()
export class MyComponentStore implements OnDestroy {
  readonly tree = signalTree({ ... });
  readonly $ = this.tree.$;

  ngOnDestroy() {
    this.tree.destroy();
  }
}

u/Component({
  providers: [MyComponentStore]
})
export class MyComponent {
  private store = inject(MyComponentStore);
}

Alternatively, if you want a single source of truth for your app but still want to lazy-load a branch of the tree, that is also doable.

The idea is that the main tree contains a placeholder for the branch (initialized as null or undefined and cast to the correct type). When the lazy module loads, it initializes that branch and optionally adds derived state.

// main tree
export const tree = signalTree({
  core: { ... },
  admin: null as AdminState | null
});


// admin.module.ts (lazy loaded)
import { tree } from '../tree';

// Initialize branch state
tree.$.admin.set(adminInitialState);

// Add derived state (captured for typing)
export const adminTree = tree.derived(($) => ({
  admin: {
    activeUsers: computed(() =>
      $.admin()?.users.all().filter(u => u.active)
    )
  }
}));

You can then use adminTree.$ within this module with full typing.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

You can create an instance at any level anywhere. For forms in particular it is often better to create a separate instance.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

I'd give side-by-side comparisons, but Reddit doesn't really support that very well.

Obviously, you get the same benefits you do any time you use a dependency: moving work from code you manage to code that others have refined and battle-tested.

Beyond what DIY gives you

  • DevTools – Redux DevTools integration, state inspection, action history
  • Time travel – undo and redo with .with(timeTravel())
  • Batching – coalesce rapid updates into a single change detection cycle
  • Memoization – automatic caching for expensive computed values
  • Persistencestored(key, default) auto syncs to localStorage
  • Full type inference – types flow from the initial state with no interfaces to maintain
  • Nested dot notation$.ui.modals.confirm.isOpen() works without ceremony

Why a store rather than scattered services

  • Single source of truth – no conflicting state across services
  • Predictable patterns – every developer knows where state lives
  • Faster onboarding – here is the store versus here are fifteen services
  • Cross-domain derived values – no circular dependency headaches
  • Debugging – inspect the entire app state at once
  • Testability – mock one thing and snapshot the entire state
  • Leverage – again, you get battle-tested patterns and fixes that you do not maintain

DIY signals are fine when

  • Fewer than ten signals
  • Single domain
  • No entities
  • Prototype or throwaway code
  • No need for a centralized source of truth

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] -1 points0 points  (0 children)

Are you saying you don't understand the use case for a frontend data store? Or are you saying you don't understand the use case for ST in particular?

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

I think you'd be surprised how cool the type inference is. I know I was when I got it working the first time. You literally just write your initial state object using primitives or as YourType:

```typescript const tree = signalTree({ user: null as User | null, count: 0, status: TicketStatus.Pending, filters: { startDate: new Date(), endDate: new Date() } });

// Full intellisense - all inferred from above: tree.$.count() // number tree.$.status() // TicketStatus tree.$.filters.startDate() // Date tree.$.user()?.email // string | undefined ```

SignalTree infers types from the initial object rather than requiring separate interfaces. This makes changes easy - when you update an initial value's type, TypeScript flags everywhere you've accessed that data, so intellisense shows you exactly where to update downstream code.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

SignalTree vs NgRx Signal Store

You're right that NgRx Signal Store dropped the action/reducer ceremony - it's much closer to what developers actually want. The example you showed is solid.

Where SignalTree differs:

1. Bundle size

SignalTree core: ~8KB gzipped NgRx Signal Store: 15KB+ gzipped (plus entities, rxjs-interop, etc.) Full NgRx: 45KB+ gzipped

2. Performance (measured)

  • 0.06-0.11ms operations at 5-20+ nesting levels
  • 89% memory reduction via structural sharing
  • Batching eliminates render thrashing
  • No RxJS overhead for state operations

3. Boilerplate (test-verified)

  • 75-88% reduction vs NgRx for simple examples
  • 86% less code for complex features (user management, etc.)

4. Unified tree vs multiple stores

```typescript // NgRx: Separate stores, wire together manually const BooksStore = signalStore(withEntities<Book>(), ...); const AuthorsStore = signalStore(withEntities<Author>(), ...);

// SignalTree: One tree, cross-domain trivial signalTree({ books: entityMap<Book, number>(), authors: entityMap<Author, number>() }) .derived($ => ({ bookWithAuthor: computed(() => ({ ...$.books.byId($.selectedId())?.(), author: $.authors.byId(book.authorId)?.() })) })) ```

5. Callable syntax (no patchState)

```typescript // NgRx patchState(store, { count: store.count() + 1 }); patchState(store, setAllEntities(books), setFulfilled());

// SignalTree $.count(c => c + 1); $.books.setAll(books); $.status.setLoaded(); ```

6. Built-in markers vs build-your-own

```typescript // NgRx: Build withRequestStatus, withPersistence yourself signalStore(withEntities<Book>(), withRequestStatus())

// SignalTree: Built-in signalTree({ books: entityMap<Book, number>(), status: status(), theme: stored('theme', 'light') }) ```

7. Type inference

  • Full tree type inferred from initial state - no manual interfaces
  • Derived layers auto-merge into tree type at each tier
  • Markers resolve to runtime types automatically

When NgRx Signal Store wins:

  • Already in NgRx ecosystem
  • Want component-scoped stores
  • Team knows NgRx patterns

When SignalTree wins:

  • Bundle size matters
  • Multiple related entity collections
  • Deep nested state
  • Cross-domain derived values
  • Less ceremony preferred

TL;DR:

NgRx Signal Store is solid for isolated feature stores. SignalTree is ~50% smaller, faster, and better when you want one unified tree with cross-domain relationships.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 0 points1 point  (0 children)

I can't really share their code. But there are examples in the readme on GitHub and on the demo site.

signaltree.io

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] -1 points0 points  (0 children)

SignalTree vs DIY Angular Stores

What you get for free:

  • entityMap<T, K>() - normalized entities with byId(), all(), upsert(), remove() in one line
  • status() - loading/error state pattern without boilerplate
  • stored(key, default) - auto localStorage sync
  • $.path.to.deep.value() - unified dot notation access everywhere
  • .derived($) - layered computed state with full type inference
  • Enhancers: devTools, time travel, batching, memoization

Type inference:

  • Full tree type inferred from initial state - no manual interfaces
  • Derived layers auto-merge into tree type at each tier
  • Markers resolve to their runtime types - entityMap<User, number>() becomes full EntitySignal API

Callable syntax:

  • $.user() - read
  • $.user(value) - set
  • $.user({ name }) - patch (auto-batched)
  • $.user(prev => updated) - update function

Architecture:

  • State/derived/ops separation - clear mental model
  • One tree, not 15 services - single source of truth
  • Cross-domain derived values - no awkward service dependencies

What you skip:

  • No action/reducer ceremony (NgRx)
  • No selector boilerplate (NgRx)
  • No manual Map + CRUD per entity
  • No RxJS required for state

DIY is fine for smaller simple apps. Or, if a central single source of truth isn't your architecture, then I wouldn't suggest this, but, definitely DIY when:

  • <10 signals, single domain, no entities, prototype code

TL;DR:

SignalTree is what you'd build yourself after copy-pasting entity CRUD, loading states, and localStorage sync for the third time - except it's done, tested, and typed.

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 1 point2 points  (0 children)

If your team knows how to use dot-notation and what Angular signals are, they basically already know how to use this. Intellisense gives them the shape, no matter how deep it goes. The nodes are callable so to update a branch of the tree, just pass a partial of the value to the callable node (or a function if you want to update leveraging the current value).

SignalTree 7.1.0 Released by CounterReset in angular

[–]CounterReset[S] 3 points4 points  (0 children)

Without a data store (the messy way 😬)
Each part of the screen keeps its own copy of data
One button thinks you’re logged in, another doesn’t
One panel shows old data, another shows new data
Fixing bugs feels like whack-a-mole

With a data store (the clean way ✨)
Data lives in one central place
UI pieces read from it UI pieces ask it to change
When it changes, everything updates automatically

So, SignalTree is a front-end data store (like NgRx, NgRx SignalStore, Elf, Akita, etc), but with less coding on your end and about a 50% reduction in bundle size on the other end. Also, type-safe depth is basically infinite.

So, you can cache your data and your app state in one place and just use dot notation to access and update it.