Skip to content

Commit d7d19a7

Browse files
authored
Merge pull request #15 from reduxjs/linked-signal-fixes
Linked signal fixes
2 parents bfb53d7 + 6997e96 commit d7d19a7

File tree

8 files changed

+3039
-1482
lines changed

8 files changed

+3039
-1482
lines changed

docs/introduction/getting-started.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ description: "Introduction > Getting Started: First steps with Angular Redux"
1212

1313
## Installation
1414

15-
Angular Redux 8.x requires **Angular 17.3 or later**, in order to make use of Angular Signals.
15+
Angular Redux 2.x requires **Angular 19 or later**, in order to make use of Angular Signals.
1616

1717
### Installing with `ng add`
1818

package.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@
2424
},
2525
"private": true,
2626
"dependencies": {
27-
"@angular/animations": "^18.2.0",
28-
"@angular/common": "^18.2.0",
29-
"@angular/compiler": "^18.2.0",
30-
"@angular/core": "^18.2.0",
31-
"@angular/forms": "^18.2.0",
32-
"@angular/platform-browser": "^18.2.0",
33-
"@angular/platform-browser-dynamic": "^18.2.0",
34-
"@angular/router": "^18.2.0",
27+
"@angular/animations": "^19.0.0",
28+
"@angular/common": "^19.0.0",
29+
"@angular/compiler": "^19.0.0",
30+
"@angular/core": "^19.0.0",
31+
"@angular/forms": "^19.0.0",
32+
"@angular/platform-browser": "^19.0.0",
33+
"@angular/platform-browser-dynamic": "^19.0.0",
34+
"@angular/router": "^19.0.0",
3535
"@reduxjs/toolkit": "^2.2.7",
3636
"redux": "^5.0.1",
3737
"rxjs": "~7.8.0",
3838
"tslib": "^2.3.0",
39-
"zone.js": "~0.14.10"
39+
"zone.js": "~0.15.0"
4040
},
4141
"devDependencies": {
42-
"@angular-devkit/build-angular": "^18.2.2",
43-
"@angular/cli": "^18.2.2",
44-
"@angular/compiler-cli": "^18.2.0",
42+
"@angular-devkit/build-angular": "^19.0.1",
43+
"@angular/cli": "^19.0.1",
44+
"@angular/compiler-cli": "^19.0.0",
4545
"@testing-library/angular": "^17.3.1",
4646
"@testing-library/dom": "^10.0.0",
4747
"@testing-library/jest-dom": "^6.4.8",
@@ -54,7 +54,7 @@
5454
"jasmine-core": "~5.2.0",
5555
"jest": "^29.7.0",
5656
"jest-environment-jsdom": "^29.7.0",
57-
"ng-packagr": "^18.2.0",
57+
"ng-packagr": "^19.0.1",
5858
"prettier": "^3.3.3",
5959
"ts-jest": "^29.2.5",
6060
"typescript": "~5.5.2"

projects/angular-redux/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Performant and flexible.
99

1010
## Installation
1111

12-
Angular Redux requires **Angular 17.3 or later**.
12+
Angular Redux requires **Angular 19 or later**.
1313

1414
### Installing with `ng add`
1515

projects/angular-redux/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reduxjs/angular-redux",
3-
"version": "1.0.1",
3+
"version": "2.0.0",
44
"keywords": [
55
"angular",
66
"redux"
@@ -11,8 +11,8 @@
1111
"repository": "github:reduxjs/angular-redux",
1212
"bugs": "https://github.com/reduxjs/angular-redux/issues",
1313
"peerDependencies": {
14-
"@angular/common": ">=17.3.0",
15-
"@angular/core": ">=17.3.0",
14+
"@angular/common": ">=19.0.0",
15+
"@angular/core": ">=19.0.0",
1616
"@reduxjs/toolkit": "^2.2.7",
1717
"redux": "^5.0.0"
1818
},

projects/angular-redux/src/lib/inject-selector.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
DestroyRef,
55
effect,
66
inject,
7+
linkedSignal,
78
Signal,
89
signal,
910
} from '@angular/core';
@@ -90,7 +91,7 @@ export function createSelectorInjection(): InjectSelector {
9091

9192
const { store, subscription } = reduxContext;
9293

93-
const selectedState = signal(selector(store.getState()));
94+
const selectedState = linkedSignal(() => selector(store.getState()));
9495

9596
const unsubscribe = subscription.addNestedSub(() => {
9697
const data = selector(store.getState());
@@ -105,7 +106,7 @@ export function createSelectorInjection(): InjectSelector {
105106
unsubscribe();
106107
});
107108

108-
return selectedState;
109+
return selectedState.asReadonly();
109110
};
110111

111112
Object.assign(injectSelector, {

projects/angular-redux/src/tests/inject-selector-and-dispatch.spec.ts

+42-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component } from '@angular/core';
1+
import { Component, input } from '@angular/core';
22
import { render, waitFor } from '@testing-library/angular';
33
import '@testing-library/jest-dom';
44
import { configureStore, createSlice } from '@reduxjs/toolkit';
@@ -100,3 +100,44 @@ it('should show a value dispatched during ngOnInit', async () => {
100100

101101
await waitFor(() => expect(getByText('Count: 1')).toBeInTheDocument());
102102
});
103+
104+
it("should not throw an error on a required input passed to the selector's fn", async () => {
105+
const store = configureStore({
106+
reducer: {
107+
counter: counterSlice.reducer,
108+
},
109+
});
110+
111+
@Component({
112+
selector: 'app-count-and-add',
113+
standalone: true,
114+
template: `
115+
<button aria-label="Increment value" (click)="dispatch(increment())">
116+
Increment
117+
</button>
118+
<p>Count: {{ count() }}</p>
119+
`,
120+
})
121+
class CountAndAdd {
122+
dispatch = injectDispatch();
123+
increment = counterSlice.actions.increment;
124+
addBy = input.required<number>();
125+
count = injectSelector((state: any) => state.counter.value + this.addBy());
126+
}
127+
128+
@Component({
129+
selector: 'app-root',
130+
imports: [CountAndAdd],
131+
standalone: true,
132+
template: '<app-count-and-add [addBy]="12"/>',
133+
})
134+
class App {}
135+
136+
const { getByText, getByLabelText } = await render(App, {
137+
providers: [provideRedux({ store })],
138+
});
139+
140+
await waitFor(() => expect(getByText('Count: 12')).toBeInTheDocument());
141+
await user.click(getByLabelText('Increment value'));
142+
await waitFor(() => expect(getByText('Count: 13')).toBeInTheDocument());
143+
});

projects/angular-redux/src/tests/inject-selector.spec.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,14 @@ describe('injectSelector lifecycle interactions', () => {
119119
}
120120

121121
@Component({
122-
selector: 'app-root',
123-
standalone: true,
124-
imports: [Child],
125-
template: `
122+
selector: 'app-root',
123+
imports: [Child],
124+
template: `
126125
@if (count() === 1) {
127126
<child-root />
128127
}
129-
`,
130-
})
128+
`
129+
})
131130
class Parent {
132131
contextVal = injectReduxAndAssignApp();
133132
count = injectNormalSelector((s) => s.count);
@@ -167,15 +166,14 @@ describe('injectSelector lifecycle interactions', () => {
167166
}
168167

169168
@Component({
170-
selector: 'app-root',
171-
standalone: true,
172-
imports: [Child],
173-
template: `
169+
selector: 'app-root',
170+
imports: [Child],
171+
template: `
174172
@if (count() === 0) {
175173
<child-root />
176174
}
177-
`,
178-
})
175+
`
176+
})
179177
class Parent {
180178
contextVal = injectReduxAndAssignApp();
181179
count = injectNormalSelector((s) => s.count);
@@ -265,14 +263,13 @@ describe('performance optimizations and bail-outs', () => {
265263
}
266264

267265
@Component({
268-
selector: 'app-root',
269-
standalone: true,
270-
imports: [Comp, Comp2],
271-
template: `
266+
selector: 'app-root',
267+
imports: [Comp, Comp2],
268+
template: `
272269
<app-comp />
273270
<app-other />
274-
`,
275-
})
271+
`
272+
})
276273
class App {}
277274

278275
await render(App, {
@@ -322,6 +319,6 @@ describe('performance optimizations and bail-outs', () => {
322319
store.dispatch({ type: '' });
323320

324321
await waitFor(() => expect(selector).toHaveBeenCalledTimes(2));
325-
expect(renderedItems.length).toEqual(2);
322+
expect(renderedItems.length).toEqual(1);
326323
});
327324
});

0 commit comments

Comments
 (0)