-
-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(signals): add withLinkedState()
#4818
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
base: main
Are you sure you want to change the base?
feat(signals): add withLinkedState()
#4818
Conversation
✅ Deploy Preview for ngrx-io ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for ngrx-site-v19 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
7bc804e
to
7b04ea5
Compare
Generates and adds the properties of a `linkedSignal` to the store's state. ## Usage Notes: ```typescript const UserStore = signalStore( withState({ options: [1, 2, 3] }), withLinkedState(({ options }) => ({ selectOption: () => options()[0] ?? undefined })) ); ``` The resulting state is of type `{ options: number[], selectOption: number | undefined }`. Whenever the `options` signal changes, the `selectOption` will automatically update. For advanced use cases, `linkedSignal` can be called within `withLinkedState`: ```typescript const UserStore = signalStore( withState({ id: 1 }), withLinkedState(({ id }) => ({ user: linkedSignal({ source: id, computation: () => ({ firstname: '', lastname: '' }) }) })) ) ``` ## Implementation Notes We do not want to encourage wrapping larger parts of the state into a `linkedSignal`. This decision is primarily driven by performance concerns. When the entire state is bound to a single signal, any change - regardless of which part - - is tracked through that one signal. This means all direct consumers are notified, even if only a small slice of the state actually changed. Instead, each root property of the state should be a Signal on its own. That's why the design of `withLinkedState` cannot represent be the whole state.
7b04ea5
to
4337ebf
Compare
Co-authored-by: michael-small <33669563+michael-small@users.noreply.github.com>
@markostanimirovic, @timdeschryver this one has been rebased to main and is now ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
@markostanimirovic, @timdeschryver, I think I've applied all necessary changes (including the docs). We can do another round of review. |
- Functions that return values, which the SignalStore wraps automatically into a `linkedSignal()`, or | ||
- `WritableSignal`, which the user can create with `linkedSignal()`. | ||
|
||
### Implicit Linking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all three of these ###
h3's should be ##
h2's due to the precedence of header sizes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
|
||
<code-example header="book-store.ts"> | ||
|
||
import { signalStore, linkedSignal } from '@ngrx/signals'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { signalStore, linkedSignal } from '@ngrx/signals'; | |
import { linkedSignal } from '@angular/core'; | |
import { signalStore, withLinkedState, withState } from '@ngrx/signals'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linkedSignal
should be imported from @angular/core
, not @ngrx/signals
. withLinkedState
import is missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, good catch, thanks.
Suggestion updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
<code-example header="book-store.ts"> | ||
|
||
import { signalStore, linkedSignal, withLinkedState } from '@ngrx/signals'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { signalStore, linkedSignal, withLinkedState } from '@ngrx/signals'; | |
import { linkedSignal } from '@angular/core'; | |
import { signalStore, withLinkedState, withState } from '@ngrx/signals'; |
edit: imports respectively from Angular itself and the store
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
safeSelectedOption: linkedSignal({ | ||
source: selectedIx, | ||
computation: (sel, previous) => { | ||
const ix = selectedIx(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be sel
I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (example changed)
safeSelectedOption: linkedSignal({ | ||
source: selectedIx, | ||
computation: (sel, previous) => { | ||
const ix = selectedIx(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be sel
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (example changed)
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
|
||
<code-example header="book-store.ts"> | ||
|
||
import { signalStore, linkedSignal } from '@ngrx/signals'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linkedSignal
should be imported from @angular/core
, not @ngrx/signals
. withLinkedState
import is missing
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
projects/ngrx.io/content/guide/signals/signal-store/linked-state.md
Outdated
Show resolved
Hide resolved
…te.md Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
…te.md Co-authored-by: Michael Small <85510853+msmallest@users.noreply.github.com>
…te.md Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
…te.md Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
…te.md Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
…te.md Co-authored-by: Marko Stanimirović <markostanimirovic95@gmail.com>
@markostanimirovic, @michael-small Wow, what a review. Corrections outnumbered the actual code by a margin...a review for the records 😅 . I seriously owe you both a drink for that. I've applied all the changes and double-checked. I also updated the jsdoc for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This is a non-breaking feature to support
linkedSignal
.This branch is based on #4795 which has to be merged first.
Please read the comment in #4781
withLinkedState
generates and adds the properties of alinkedSignal
to the store's state.Usage Notes:
The resulting state is of type
{ options: number[], selectOption: number | undefined }
.Whenever the
options
signal changes, theselectOption
will automatically update.For advanced use cases,
linkedSignal
can be called withinwithLinkedState
:Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Closes #4781
What is the new behavior?
Does this PR introduce a breaking change?
Other information