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

refactor(store): add action registry #2224

Merged
merged 7 commits into from
Oct 14, 2024
Merged

Conversation

arturovt
Copy link
Member

@arturovt arturovt commented Oct 1, 2024

This adds a new internal construct, called the ActionRegistry.
The ActionRegistry facilitates the registration, and recall, of all handlers associated with an action type.
The default handler registered for each @Action within a state is now responsible for handling the return value of the user's handler within the state and the cancellation logic for the handler.

Copy link

nx-cloud bot commented Oct 1, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit ddbed56. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 4 targets

Sent with 💌 from NxCloud.

Copy link

pkg-pr-new bot commented Oct 1, 2024

Open in Stackblitz

@ngxs/devtools-plugin

yarn add https://pkg.pr.new/@ngxs/devtools-plugin@2224.tgz

@ngxs/form-plugin

yarn add https://pkg.pr.new/@ngxs/form-plugin@2224.tgz

@ngxs/hmr-plugin

yarn add https://pkg.pr.new/@ngxs/hmr-plugin@2224.tgz

@ngxs/router-plugin

yarn add https://pkg.pr.new/@ngxs/router-plugin@2224.tgz

@ngxs/storage-plugin

yarn add https://pkg.pr.new/@ngxs/storage-plugin@2224.tgz

@ngxs/store

yarn add https://pkg.pr.new/@ngxs/store@2224.tgz

@ngxs/websocket-plugin

yarn add https://pkg.pr.new/@ngxs/websocket-plugin@2224.tgz

commit: ddbed56

Copy link

bundlemon bot commented Oct 1, 2024

BundleMon

Files updated (1)
Status Path Size Limits
fesm2022/ngxs-store.mjs
100.32KB (+152B +0.15%) 101KB / +0.5%
Unchanged files (5)
Status Path Size Limits
fesm2022/ngxs-store-internals.mjs
11.36KB 13KB / +0.5%
fesm2022/ngxs-store-internals-testing.mjs
6.83KB 7KB / +0.5%
fesm2022/ngxs-store-operators.mjs
6.22KB 7KB / +0.5%
fesm2022/ngxs-store-plugins.mjs
2.04KB 3KB / +0.5%
fesm2022/ngxs-store-experimental.mjs
1.4KB 2KB / +0.5%

Total files change +152B +0.12%

Groups updated (2)
Status Path Size Limits
@ngxs/store(esm2022)[gzip]
./esm2022/**/*.mjs
222.11KB (+1.27KB +0.58%) +1%
@ngxs/store(fesm2022)[gzip]
./fesm2022/*.mjs
30.63KB (-87B -0.28%) +1%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

Copy link

bundlemon bot commented Oct 1, 2024

BundleMon (NGXS Plugins)

Unchanged files (9)
Status Path Size Limits
Plugins(fesm2022)[gzip]
storage-plugin/fesm2022/ngxs-storage-plugin.m
js
4.15KB +0.5%
Plugins(fesm2022)[gzip]
router-plugin/fesm2022/ngxs-router-plugin.mjs
3.2KB +0.5%
Plugins(fesm2022)[gzip]
websocket-plugin/fesm2022/ngxs-websocket-plug
in.mjs
2.64KB +0.5%
Plugins(fesm2022)[gzip]
hmr-plugin/fesm2022/ngxs-hmr-plugin.mjs
2.61KB +0.5%
Plugins(fesm2022)[gzip]
form-plugin/fesm2022/ngxs-form-plugin.mjs
2.59KB +0.5%
Plugins(fesm2022)[gzip]
devtools-plugin/fesm2022/ngxs-devtools-plugin
.mjs
2.23KB +0.5%
Plugins(fesm2022)[gzip]
logger-plugin/fesm2022/ngxs-logger-plugin.mjs
2.09KB +0.5%
Plugins(fesm2022)[gzip]
storage-plugin/fesm2022/ngxs-storage-plugin-i
nternals.mjs
875B +0.5%
Plugins(fesm2022)[gzip]
router-plugin/fesm2022/ngxs-router-plugin-int
ernals.mjs
411B +0.5%

No change in files bundle size

Unchanged groups (1)
Status Path Size Limits
All Plugins(fesm2022)[gzip]
./-plugin/fesm2022/.mjs
20.76KB +0.5%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

Copy link

bundlemon bot commented Oct 1, 2024

BundleMon (Integration Projects)

Files updated (3)
Status Path Size Limits
Main bundles(Gzip)
hello-world-ng18/dist-integration/browser/mai
n-(hash).js
71.81KB (+64B +0.09%) +1%
Main bundles(Gzip)
hello-world-ng16/dist-integration/main.(hash)
.js
67.74KB (+47B +0.07%) +1%
Main bundles(Gzip)
hello-world-ng17/dist-integration/main.(hash)
.js
68.71KB (+47B +0.07%) +1%

Total files change +158B +0.07%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

Copy link
Member

@markwhitfeld markwhitfeld left a comment

Choose a reason for hiding this comment

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

Great first step of the refactoring, but see my comments for the change in the handlers.

packages/store/src/actions/action-registry.ts Outdated Show resolved Hide resolved
packages/store/src/internal/state-factory.ts Outdated Show resolved Hide resolved
Copy link
Member

@markwhitfeld markwhitfeld left a comment

Choose a reason for hiding this comment

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

Please see comments in this review and the suggested code changes from the previous review.

};
}

private _createActionHandler(actionMeta: InvokableActionHandlerMetaData) {
Copy link
Member

Choose a reason for hiding this comment

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

As mentioned in the second comment of my previous review, the creation of the handler which in turn creates the state context, should stay outside of the action registry (in the StateFactory, for now).
Also the result handling logic should remain where it was in the StateFactory.

Comment on lines 22 to 25
interface InvokableActionHandlerMetaData extends ɵActionHandlerMetaData {
path: string;
instance: any;
}
Copy link
Member

Choose a reason for hiding this comment

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

This data structure should no longer be needed for the registry.
The registry should be super simple, only storing the handler functions, constructed and provided by the StateFactory.
The place in the StateFactory where the handler is created should contain enough access from its closure to be able to do all that it needs.

packages/store/src/actions/dispatched-actions.ts Outdated Show resolved Hide resolved
Copy link
Member

@markwhitfeld markwhitfeld left a comment

Choose a reason for hiding this comment

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

Great work!!!!

Copy link

codeclimate bot commented Oct 14, 2024

Code Climate has analyzed commit ddbed56 and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 96.4% (50% is the threshold).

This pull request will bring the total coverage in the repository to 95.3% (-0.1% change).

View more on Code Climate.

@arturovt arturovt merged commit d6b076c into master Oct 14, 2024
18 checks passed
@arturovt arturovt deleted the refactor/action-registry branch October 14, 2024 19:13
@hakimio
Copy link
Contributor

hakimio commented Oct 23, 2024

@arturovt
Sorry to bother you, just wanted to ask for a small favor.
@angular-ru/ngxs library relies heavily on ngxs/store internals. After your recent ngxs refactoring, I have updated @angular-ru/ngxs to be compatible with ngxs v18.1.3 (Angular-RU/angular-ru-sdk#1527) but since I'm not too familiar with ngxs inner workings, I imagine the code might be suboptimal. When you have some time, could you take a look at the PR and specifically at the @DataAction decorator (docs) and let me know if the code could be improved?

@arturovt
Copy link
Member Author

I would first remove NgxsDataInjector.ngZone?.run(...) because it’s not required for NGXS. It doesn’t care whether dispatched events come from the Angular zone or not, as the state updates are decoupled from the dispatcher, and the new state always occurs within the Angular zone.

Since states are decorated with @StateRepository, that decorator could return a new constructor function, which is always called within the Angular injection context. You can capture the injector through this[SomeInternalInjectorSymbol] = inject(Injector). The descriptor.value function now has access to this[SomeInternalInjectorSymbol] and can retrieve anything from the "app" injector as well as from the globally configured static injector. However, static properties wouldn't work properly for server-side rendered apps. If a new app is rendered for the first HTTP request and then a second request comes in, the newly bootstrapped app would overwrite static properties.

@hakimio
Copy link
Contributor

hakimio commented Oct 23, 2024

Thanks for the suggestions.

You can capture the injector through this[SomeInternalInjectorSymbol] = inject(Injector).

Basically this type of injector usage instead of global static injector could fix the mentioned SSR issue?

What do you think about the operation caching used in the @DataAction decorator? Is it necessary and can it be simplified?

Is it ok to use hydrateActionMetasMap() in the decorator or could there be some better/simpler way to register the handlers?

@arturovt
Copy link
Member Author

I think it might be better to find a way to register actions without relying on internal APIs, which are likely to change in the future. It seems that the DataAction decorator updates the metadata only when the method is called for the first time, but it should behave like the Action decorator and update the metadata immediately. This would eliminate the need to call hydrateActionMetasMap.

@hakimio
Copy link
Contributor

hakimio commented Oct 25, 2024

@arturovt thank you for your insights.
The @Action decorator is definitely a good example of how @DataAction could be implemented.

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.

3 participants