Skip to content

docs(ngrx/signals): mention re-use of withComputed/withMethods #4780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions projects/ngrx.io/content/guide/signals/signal-store/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,38 @@ export const BooksStore = signalStore(

</code-example>

<div class="alert is-helpful">

It may be necessary for a computed in a `withComputed` feature to need to reference another computed value,
or a method in a `withMethods` feature to refer to another method. To do so, either:

(A) Create another `withComputed` feature or `withMethods` feature, or

(B) Break out the common piece with a helper `const`.

```ts
export const BooksStore = signalStore(
withState(initialState),
// 👇 Accessing previously defined state signals and properties.
withComputed(({ books, filter }) => ({
booksCount: computed(() => books().length),
sortDirection: computed(() => filter.order() === 'asc' ? 1 : -1),
})),
// 👇 (A) Also access previously defined computed properties (or functions).
withComputed(({ books, sortDirection }) => {
Comment on lines +301 to +306
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was writing this page, I assumed that readers are familiar with core JavaScript concepts, so they're aware that () => ({}) is a sugar syntax for () => { return {} }.

I'm not a fan of using multiple withComputed calls. The native JS way should be preferred and recommended in my opinion:

withComputed({ books, filter }) => {
  const booksCount = computed(() => books().length);
  const sortDirection = computed(() => /* ... */);
  // ...
  
  return { /* export public computed signals */ };
}),

Additionally, it provides the ability not to export private computed signals if necessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was writing this page, I assumed that readers are familiar with core JavaScript concepts, so they're aware that () => ({}) is a sugar syntax for () => { return {} }.

(sorry if this is overselling but I have thought on this a lot) In retrospect that made sense when I learned this after seeking it out, but I have seen people with this same problem frequently across different platforms. It has even been news to some GDEs who make signal store content. I can't speak for why the various other people didn't recognize this sugar syntax, but for me I just think going from a class based store to a function based one has a lot of little moments like this. And this point for people who wonder about this seems to solve a large hurdle with a small tip.

I'm not a fan of using multiple withComputed calls. The native JS way should be preferred and recommended in my opinion:

I agree. That said, in the places this has come up, it feels like 2/3 of people are like "great, no need for another withComputed" and the other 1/3 are like "that's cool that the const method works good but I prefer multiple withComputed". So in my opinion, I feel like this const helper restructuring is worth a tip somewhere and that people who prefer multiple withComputed are just going to keep doing that lol.

Copy link
Author

@michael-small michael-small May 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about having this PR as still adding this tip with respect to the native const approach, but dropping the repeated withComputed/withMethods reference?

@timdeschryver / @rainerhahnekamp you both suggested having them both, so I'm curious if my proposed compromise which tentatively drop the repeated withComputed sounds legit.

// 👇 (B) Define helper functions (or computeds).
const sortBooks = (direction: number) =>
books().toSorted((a, b) => direction * a.title.localeCompare(b.title));

return {
sortedBooks: computed(() => sortBooks(sortDirection())),
reversedBooks: computed(() => sortBooks(-1 * sortDirection())),
};
}),
);
```
</div>

### Reactive Store Methods

In more complex scenarios, opting for RxJS to handle asynchronous side effects is advisable.
Expand Down