Skip to content
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

feat: make signalStore entries starting with _ not available outside of the store (interface, first level) #4309

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mauriziocescon
Copy link

@mauriziocescon mauriziocescon commented Apr 29, 2024

Summary

As (more or less) mentioned in #4283 and #4210, providers returned by the factory signalStore are currently lacking some sort on encapsulation.

As I see it, there are 2 problems:

  1. there is no way to declare that some slices of the state / computeds / methods are private, meaning that they are not meant to be used by consumers,
  2. patchState and getState are defined as utilities applicable on both objects returned by signalStore and signalState. This is good for reusability, but it comes with the undesired side effect of making a signalStore patchable anywhere (like in components consuming it). Considering the store mental model, patchState and getState should probably be available only in methods / hooks.

The goal of this PR is targeting point 1, following (hopefully) the suggestion in #4210 (comment). Here is a use case where the feature would come in handy (there are many others):

/**
 * Modified example at https://ngrx.io/guide/signals/signal-store#putting-it-all-together
 * where the store is defined as a global provider.
 *
 * In this case, you probably don't want to have loadByQuery callable from consumers
 * because you don't want to have more than one subscription.
 */
const BooksStore = signalStore(
  { providedIn: 'root' },
  withState({/* ... */ filter: { query: '', order: 'asc' } }),
  // ...
  withMethods((store, booksService = inject(BooksService)) => ({
      // not callable from components injecting BooksStore
      _loadByQuery: rxMethod<string>(/* ... */),
    }),
  ),
  withHooks({
    onInit(store): void {
      store._loadByQuery(store.filter.query);
    },
  }),
);

With this PR, prefixing any root slice of the state / computed / method by _ should make it unavailable (ts interface) in the returned signalStore provider.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Documentation content changes
[ ] Other... Please describe:

What is the current behavior?

Providers returned by the factory signalStore are currently lacking encapsulation

Partially address #4283 and #4210

What is the new behavior?

Prefixing any root slice of the state / computed / method by _ should make it unavailable (ts interface) in the returned signalStore provider.

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Copy link

netlify bot commented Apr 29, 2024

Deploy Preview for ngrx-io canceled.

Built without sensitive environment variables

Name Link
🔨 Latest commit c753574
🔍 Latest deploy log https://app.netlify.com/sites/ngrx-io/deploys/662f687f69613a00083fa6cd

@rainerhahnekamp
Copy link
Contributor

Hey @mauriziocescon, good stuff!

In your case, would you also have to prefix the properties in the state with _ to make them unpatchable from the outside?

Have you considered combining #4210 so that the whole state would be patchable?

By combining, I meant that for the state we could use signalStore({readonly: true}) and methods and computed stick to _.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants