@adimm/x-injection-reactjs 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -123
- package/dist/index.cjs +84 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -133
- package/dist/index.d.ts +97 -133
- package/dist/index.js +63 -129
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
2
|
xInjection ReactJS <a href="https://www.npmjs.com/package/@adimm/x-injection-reactjs" target="__blank"><img src="https://badgen.net/npm/v/@adimm/x-injection-reactjs"></a>
|
|
3
|
-
<img src="https://badgen.net/npm/license/@adimm/x-injection-reactjs">
|
|
4
3
|
<a href="https://app.codecov.io/gh/AdiMarianMutu/x-injection-reactjs" target="__blank"><img src="https://badgen.net/codecov/c/github/AdiMarianMutu/x-injection-reactjs"></a>
|
|
4
|
+
<img src="https://badgen.net/npm/license/@adimm/x-injection-reactjs">
|
|
5
5
|
</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -19,24 +19,32 @@ xInjection ReactJS <a href="https://www.npmjs.com/package/@adimm/x-injection-rea
|
|
|
19
19
|
- [Installation](#installation)
|
|
20
20
|
- [TypeScript Configuration](#typescript-configuration)
|
|
21
21
|
- [Getting Started](#getting-started)
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
- [Quick Start](#quick-start)
|
|
23
|
+
- [Conventions](#conventions)
|
|
24
|
+
- [Component Module](#component-module)
|
|
25
|
+
- [Component Service](#component-service)
|
|
26
|
+
- [How to tie a `ProviderModule` to a `Component`?](#how-to-tie-a-providermodule-to-a-component)
|
|
27
|
+
- [Is your component re-usable?](#is-your-component-re-usable)
|
|
28
|
+
- [Yes](#yes)
|
|
29
|
+
- [No](#no)
|
|
30
|
+
- [How to control a Child component providers from Parent component?](#how-to-control-a-child-component-providers-from-parent-component)
|
|
31
|
+
- [Override the entire Child Module](#override-the-entire-child-module)
|
|
32
|
+
- [Override only specific Child Providers](#override-only-specific-child-providers)
|
|
26
33
|
- [Hook Injection](#hook-injection)
|
|
27
34
|
- [Examples](#examples)
|
|
28
35
|
- [Composable components](#composable-components)
|
|
29
36
|
- [Unit Tests](#unit-tests)
|
|
30
37
|
- [Documentation](#documentation)
|
|
31
38
|
- [Contributing](#contributing)
|
|
39
|
+
- [Credits](#credits)
|
|
32
40
|
|
|
33
41
|
## Overview
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Each `ProviderModule` manages its _own_ container, supporting easy **decoupling** and _explicit_ control over which providers are **exported** and **imported** across modules. The global **[AppModule](https://adimarianmutu.github.io/x-injection/variables/AppModule.html)** is always available, ensuring a seamless foundation for your application's DI needs.
|
|
43
|
+
This is the _official_ [ReactJS](https://react.dev/) implementation of the [xInjection](https://github.com/AdiMarianMutu/x-injection) library.
|
|
38
44
|
|
|
39
|
-
>
|
|
45
|
+
> [!Warning]
|
|
46
|
+
>
|
|
47
|
+
> The usage of the `base` library will not be explained here, I'll assume you already know how to use the `xInjection` library, if that's not the case, please refer to the `xInjection` [Gettng Started](https://github.com/AdiMarianMutu/x-injection?tab=readme-ov-file#getting-started) section.
|
|
40
48
|
|
|
41
49
|
## Installation
|
|
42
50
|
|
|
@@ -52,6 +60,8 @@ Then install `xInjection` for React:
|
|
|
52
60
|
npm i @adimm/x-injection-reactjs
|
|
53
61
|
```
|
|
54
62
|
|
|
63
|
+
> [!Note]
|
|
64
|
+
>
|
|
55
65
|
> You may also have to install the parent library via `npm i @adimm/x-injection`
|
|
56
66
|
|
|
57
67
|
### TypeScript Configuration
|
|
@@ -69,126 +79,192 @@ Add the following options to your `tsconfig.json` to enable decorator metadata:
|
|
|
69
79
|
|
|
70
80
|
## Getting Started
|
|
71
81
|
|
|
72
|
-
|
|
82
|
+
### Quick Start
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
```tsx
|
|
85
|
+
const UserDashboardModuleBp = ProviderModule.blueprint({
|
|
86
|
+
id: 'ComponentUserDashboardModule',
|
|
87
|
+
imports: [UserModule],
|
|
88
|
+
exports: [UserModule],
|
|
89
|
+
});
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, () => {
|
|
92
|
+
const userService = useInject(UserService);
|
|
77
93
|
|
|
78
|
-
|
|
94
|
+
return (
|
|
95
|
+
<h1>
|
|
96
|
+
Hello {userService.firstName} {userService.lastName}!
|
|
97
|
+
</h1>
|
|
98
|
+
);
|
|
99
|
+
});
|
|
79
100
|
|
|
80
|
-
|
|
81
|
-
|
|
101
|
+
const App = () => {
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
<Navbar />
|
|
105
|
+
<UserDashboard />
|
|
106
|
+
<Footer />
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Conventions
|
|
82
113
|
|
|
83
|
-
|
|
114
|
+
Before continuing you should read also the [Conventions](https://github.com/AdiMarianMutu/x-injection?tab=readme-ov-file#conventions) section of the _parent_ library.
|
|
84
115
|
|
|
85
|
-
### Component
|
|
116
|
+
### Component Module
|
|
86
117
|
|
|
87
|
-
|
|
118
|
+
You should create a separate file which you'll use to declare the `blueprint` of the _component_ `module`:
|
|
88
119
|
|
|
89
|
-
|
|
120
|
+
`user-dashboard/user-dashboard.module.ts`
|
|
90
121
|
|
|
91
122
|
```ts
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
export const UserDashboardModuleBp = ProviderModule.blueprint({
|
|
124
|
+
id: 'ComponentUserDashboardModule',
|
|
125
|
+
...
|
|
126
|
+
});
|
|
127
|
+
```
|
|
96
128
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
129
|
+
> [!Note]
|
|
130
|
+
>
|
|
131
|
+
> You should also prefix the `id` of the `blueprints` with `Component` as this will help you to debug your app much more easier when something goes wrong.
|
|
101
132
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
133
|
+
### Component Service
|
|
134
|
+
|
|
135
|
+
You should create a separate file which you'll use to declare the _(main)_ `service` of the _component_.
|
|
106
136
|
|
|
107
|
-
|
|
137
|
+
`user-dashboard/user-dashboard.service.ts`
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
@Injectable()
|
|
141
|
+
export class UserDashboardService {
|
|
108
142
|
firstName: string;
|
|
109
143
|
lastName: string;
|
|
110
144
|
}
|
|
111
145
|
```
|
|
112
146
|
|
|
113
|
-
|
|
147
|
+
### How to tie a `ProviderModule` to a `Component`?
|
|
148
|
+
|
|
149
|
+
You first have to either create a `module` or `blueprint`, most of the times you'll use the `blueprint` option, if you are asking yourself how you should decide:
|
|
150
|
+
|
|
151
|
+
#### Is your component re-usable?
|
|
152
|
+
|
|
153
|
+
> Will you have **more** than **one** instance of that component?
|
|
154
|
+
|
|
155
|
+
##### Yes
|
|
114
156
|
|
|
115
|
-
|
|
157
|
+
- Then you have to use a `blueprint`, the reason can be understood by reading [this](https://github.com/AdiMarianMutu/x-injection?tab=readme-ov-file#import-behavior).
|
|
116
158
|
|
|
117
|
-
|
|
159
|
+
##### No
|
|
118
160
|
|
|
119
|
-
|
|
161
|
+
- Then you have to use a raw `module`, the reason is the opposite of the `blueprint` motive.
|
|
162
|
+
|
|
163
|
+
> [!Tip] If the above explaination is clear, please skip to the next section, otherwise keep reading.
|
|
164
|
+
|
|
165
|
+
Imagine that we have a `Button` component, clearly we'll have more than one instance of that component, this means that **each** _instance_ of the `Button` component must have its own `module`, where all the `singletons` will act as singletons _only inside_ the component **instance**.
|
|
166
|
+
|
|
167
|
+
> Therefore we leverage the `blueprint` _import_ behavior to achieve that naturally without additional overhead.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
After you created the `component module`, you can provide it to the actual component by using the [provideModuleToComponent](https://adimarianmutu.github.io/x-injection-reactjs/functions/provideModuleToComponent.html) _([HoC](https://legacy.reactjs.org/docs/higher-order-components.html))_ `method`.
|
|
120
172
|
|
|
121
173
|
```tsx
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
174
|
+
const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, (props: UserDashboardProps) => {
|
|
175
|
+
...
|
|
176
|
+
});
|
|
177
|
+
```
|
|
125
178
|
|
|
126
|
-
|
|
127
|
-
userService.lastName = lastName;
|
|
179
|
+
### How to control a Child component providers from Parent component?
|
|
128
180
|
|
|
129
|
-
|
|
181
|
+
If you need this design pattern, it is very easy to implement with `xInjection`, you actually have 2 options:
|
|
182
|
+
|
|
183
|
+
#### Override the entire Child Module
|
|
184
|
+
|
|
185
|
+
Let's say that the `Child` component has this `module`:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const ChildModuleBp = ProviderModule.blueprint({
|
|
189
|
+
id: 'ComponentChildModule',
|
|
190
|
+
providers: [ChildService],
|
|
191
|
+
exports: [ChildService],
|
|
130
192
|
});
|
|
193
|
+
```
|
|
131
194
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
195
|
+
What you can now do is to provide the `ChildService` from the `Parent` component, like this:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
const ParentModuleBp = ProviderModule.blueprint({
|
|
199
|
+
id: 'ComponentParentModule',
|
|
200
|
+
providers: [ParentService, ChildService],
|
|
201
|
+
exports: [ParentService, ChildService],
|
|
202
|
+
});
|
|
138
203
|
```
|
|
139
204
|
|
|
140
|
-
|
|
205
|
+
Then, when you are rendering the `Child` component from **within** the `Parent` component:
|
|
141
206
|
|
|
142
|
-
|
|
207
|
+
```ts
|
|
208
|
+
const ParentComponent = provideModuleToComponent(ParentModuleBp, ({ module }) => {
|
|
209
|
+
// the `module` prop is always available and automatically injected into the `props` object.
|
|
143
210
|
|
|
144
|
-
|
|
211
|
+
return <ChildComponent module={module} />;
|
|
212
|
+
});
|
|
213
|
+
```
|
|
145
214
|
|
|
146
|
-
|
|
147
|
-
export function UserInfo({ firstName, lastName }: UserInfoProps) {
|
|
148
|
-
const userService = useInject(UserService);
|
|
215
|
+
Now the `ChildComponent` will be instantiated with the `module` received from the `ParentComponent`, therefore it'll use the `ChildService` managed into the `ParentModule`.
|
|
149
216
|
|
|
150
|
-
|
|
151
|
-
|
|
217
|
+
> [!Tip]
|
|
218
|
+
>
|
|
219
|
+
> This is perfect to use when you are writing _unit tests_ and you want to mock an entire component `module`
|
|
152
220
|
|
|
153
|
-
|
|
154
|
-
}
|
|
221
|
+
#### Override only specific Child Providers
|
|
155
222
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
223
|
+
> This is the approach which you should strive to use most of the times as it is less prone to _"human error"_ than overriding the entire module.
|
|
224
|
+
|
|
225
|
+
Let's re-use the same example as the one from the above, the `ParentModule`:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const ParentModuleBp = ProviderModule.blueprint({
|
|
229
|
+
id: 'ComponentParentModule',
|
|
230
|
+
providers: [ParentService, ChildService],
|
|
231
|
+
exports: [ParentService, ChildService],
|
|
232
|
+
});
|
|
166
233
|
```
|
|
167
234
|
|
|
168
|
-
|
|
235
|
+
And now the rendering part:
|
|
169
236
|
|
|
170
|
-
|
|
237
|
+
```ts
|
|
238
|
+
const ParentComponent = provideModuleToComponent(ParentModuleBp, () => {
|
|
239
|
+
// notice that we are not using the `module` prop anymore.
|
|
240
|
+
const childService = useInject(ChildService);
|
|
171
241
|
|
|
172
|
-
|
|
242
|
+
return <ChildComponent inject={[{ provide: ChildService, useValue: childService }]} />;
|
|
243
|
+
});
|
|
244
|
+
```
|
|
173
245
|
|
|
174
|
-
|
|
246
|
+
By using the `inject` prop _(which as the `module` prop is always available)_ you'll _"swap"_ the `ChildService` provider with a [ProviderValueToken](https://adimarianmutu.github.io/x-injection/types/ProviderValueToken.html) which provides the `ChildService` **instance** instantiated by the `ParentComponent`.
|
|
175
247
|
|
|
176
|
-
|
|
248
|
+
> [!Note]
|
|
249
|
+
>
|
|
250
|
+
> If you are asking yourself `Why would I want to do that?`, that's a valid question, and most of the times you'll **not** need this feature, but sometimes, when you _compose_ components, being able to control the _providers_ of the children components becomes very useful. Check the [Composable Components](#composable-components) example to understand.
|
|
177
251
|
|
|
178
|
-
|
|
252
|
+
### Hook Injection
|
|
253
|
+
|
|
254
|
+
You already have seen in action the low-level [useInject](https://adimarianmutu.github.io/x-injection-reactjs/functions/useInject.html) hook _(take a look also at the [useInjectMany](https://adimarianmutu.github.io/x-injection-reactjs/functions/useInjectMany.html) hook)_. It is quite useful when you just have to inject quickly some dependencies into a component quite simple.
|
|
179
255
|
|
|
180
|
-
|
|
256
|
+
But, as your UI will _grow_, you'll soon discover that you may inject _more_ dependencies into a component, or even in multiple components, therefore you'll end up writing a lot of duplicated code, well, as per the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#:~:text=%22Don't%20repeat%20yourself%22,redundancy%20in%20the%20first%20place.) principle, we want to _avoid_ that.
|
|
181
257
|
|
|
182
|
-
|
|
258
|
+
This means that we can actually use the [hookFactory](https://adimarianmutu.github.io/x-injection-reactjs/functions/hookFactory.html) method to compose a _custom_ hook with access to any dependency available in the component module.
|
|
183
259
|
|
|
184
260
|
```ts
|
|
185
261
|
// The `HookWithDeps` generic type will help
|
|
186
262
|
// in making sure that the `useGenerateUserFullName` hooks params are correctly visible.
|
|
187
263
|
// The 1st generic param must be the hook params (Like `UserInfoProps`)
|
|
188
|
-
// and
|
|
264
|
+
// and the 2nd generic param must be an `array` with the providers type.
|
|
189
265
|
const useGenerateUserFullName = hookFactory({
|
|
190
266
|
// The `use` property is where you write your hook implementation.
|
|
191
|
-
use: ({ firstName, lastName, deps: [userService] }: HookWithDeps<UserInfoProps, UserService>) => {
|
|
267
|
+
use: ({ firstName, lastName, deps: [userService] }: HookWithDeps<UserInfoProps, [UserService]>) => {
|
|
192
268
|
userService.firstName = firstName;
|
|
193
269
|
userService.lastName = lastName;
|
|
194
270
|
|
|
@@ -202,7 +278,7 @@ const useGenerateUserFullName = hookFactory({
|
|
|
202
278
|
});
|
|
203
279
|
```
|
|
204
280
|
|
|
205
|
-
Now you can use it in inside any component which
|
|
281
|
+
Now you can use it in inside any component which is using a `module` which can provide the `UserService`.
|
|
206
282
|
|
|
207
283
|
```tsx
|
|
208
284
|
export function UserInfo({ firstName, lastName }: UserInfoProps) {
|
|
@@ -212,6 +288,10 @@ export function UserInfo({ firstName, lastName }: UserInfoProps) {
|
|
|
212
288
|
}
|
|
213
289
|
```
|
|
214
290
|
|
|
291
|
+
> **Note:** _If your custom hook does not accept any parameter, you can provide `void` to the 1st generic type._
|
|
292
|
+
>
|
|
293
|
+
> e.g: `use: ({ deps: [userService] }: HookWithDeps<void, [UserService]>)`
|
|
294
|
+
|
|
215
295
|
## Examples
|
|
216
296
|
|
|
217
297
|
### Composable components
|
|
@@ -249,8 +329,8 @@ export class InputboxService {
|
|
|
249
329
|
}
|
|
250
330
|
}
|
|
251
331
|
|
|
252
|
-
export const
|
|
253
|
-
|
|
332
|
+
export const InputboxModuleBp = ProviderModule.blueprint({
|
|
333
|
+
id: 'ComponentInputboxModule',
|
|
254
334
|
provides: [InputboxService],
|
|
255
335
|
exports: [InputboxService],
|
|
256
336
|
});
|
|
@@ -263,7 +343,7 @@ export interface InputboxProps {
|
|
|
263
343
|
initialValue: string;
|
|
264
344
|
}
|
|
265
345
|
|
|
266
|
-
export const Inputbox = provideModuleToComponent<InputboxProps>(
|
|
346
|
+
export const Inputbox = provideModuleToComponent<InputboxProps>(InputboxModuleBp, ({ initialValue }) => {
|
|
267
347
|
const service = useInject(InputboxService);
|
|
268
348
|
const [, setCurrentValue] = useState(initialValue);
|
|
269
349
|
service.setStateValue = setCurrentValue;
|
|
@@ -290,8 +370,8 @@ export class ListviewService {
|
|
|
290
370
|
/* Remaining fancy implementation */
|
|
291
371
|
}
|
|
292
372
|
|
|
293
|
-
export const
|
|
294
|
-
|
|
373
|
+
export const ListviewModuleBp = ProviderModule.blueprint({
|
|
374
|
+
id: 'ComponentListviewModule',
|
|
295
375
|
provides: [ListviewService],
|
|
296
376
|
exports: [ListviewService],
|
|
297
377
|
});
|
|
@@ -304,7 +384,7 @@ export interface ListviewProps {
|
|
|
304
384
|
items: any[];
|
|
305
385
|
}
|
|
306
386
|
|
|
307
|
-
export const Listview = provideModuleToComponent<ListviewProps>(
|
|
387
|
+
export const Listview = provideModuleToComponent<ListviewProps>(ListviewModuleBp, ({ items }) => {
|
|
308
388
|
const service = useInject(ListviewService);
|
|
309
389
|
|
|
310
390
|
/* Remaining fancy implementation */
|
|
@@ -338,15 +418,15 @@ export class DropdownService {
|
|
|
338
418
|
/* Remaining fancy implementation */
|
|
339
419
|
}
|
|
340
420
|
|
|
341
|
-
export const
|
|
342
|
-
|
|
421
|
+
export const DropdownModuleBp = ProviderModule.blueprint({
|
|
422
|
+
id: 'ComponentDropdownModule',
|
|
343
423
|
// It is very important that we import all the exportable dependencies from the `ListviewModule`!
|
|
344
|
-
imports: [
|
|
424
|
+
imports: [ListviewModuleBp],
|
|
345
425
|
provides: [DropdownService],
|
|
346
426
|
exports: [
|
|
347
427
|
// Let's also re-export the dependencies of the `ListviewModule` so once we import the `DropdownModule`
|
|
348
428
|
// somewhere elese, we get access to the `ListviewModule` exported dependencies as well!
|
|
349
|
-
|
|
429
|
+
ListviewModuleBp,
|
|
350
430
|
// Let's not forget to also export our `DropdownService` :)
|
|
351
431
|
DropdownService,
|
|
352
432
|
],
|
|
@@ -363,14 +443,8 @@ export interface DropdownProps {
|
|
|
363
443
|
}
|
|
364
444
|
|
|
365
445
|
export const Dropdown = provideModuleToComponent<DropdownProps>(
|
|
366
|
-
|
|
367
|
-
({
|
|
368
|
-
listviewProps,
|
|
369
|
-
initialSelectedValue,
|
|
370
|
-
// Here it is important that we get access to the contextualized module
|
|
371
|
-
// so we can forward it to the `Listview` component!
|
|
372
|
-
module,
|
|
373
|
-
}) => {
|
|
446
|
+
ListviewModuleBp,
|
|
447
|
+
({ listviewProps, initialSelectedValue }) => {
|
|
374
448
|
const service = useInject(DropdownService);
|
|
375
449
|
|
|
376
450
|
/* Remaining fancy implementation */
|
|
@@ -379,9 +453,9 @@ export const Dropdown = provideModuleToComponent<DropdownProps>(
|
|
|
379
453
|
<div className="fancy-dropdown">
|
|
380
454
|
<span>{initialSelectedValue}</span>
|
|
381
455
|
|
|
382
|
-
{/* Here we
|
|
383
|
-
|
|
384
|
-
<Listview
|
|
456
|
+
{/* Here we tell the `ListView` component to actually use the `ListviewService` instance we provide via the `useValue` property. */}
|
|
457
|
+
{/* Each `useInject(ListviewService)` used inside the `ListView` component will automatically resolve to `service.listviewService`. */}
|
|
458
|
+
<Listview {...listviewProps} inject={[{ provide: ListviewService, useValue: service.listviewService }]} />
|
|
385
459
|
</div>
|
|
386
460
|
);
|
|
387
461
|
}
|
|
@@ -418,9 +492,9 @@ export class AutocompleteService {
|
|
|
418
492
|
/* Remaining fancy implementation */
|
|
419
493
|
}
|
|
420
494
|
|
|
421
|
-
export const
|
|
422
|
-
|
|
423
|
-
imports: [
|
|
495
|
+
export const AutocompleteModuleBp = ProviderModule.blueprint({
|
|
496
|
+
id: 'ComponentAutocompleteModule',
|
|
497
|
+
imports: [InputboxModuleBp, DropdownModuleBp],
|
|
424
498
|
provides: [AutocompleteService],
|
|
425
499
|
// If we don't plan to share the internal dependencies of the
|
|
426
500
|
// Autocomplete component, then we can omit the `exports` array declaration.
|
|
@@ -437,16 +511,11 @@ export interface AutocompleteProps {
|
|
|
437
511
|
currentText: string;
|
|
438
512
|
}
|
|
439
513
|
|
|
440
|
-
export const Autocomplete = provideModuleToComponent<AutocompleteProps>(
|
|
441
|
-
AutocompleteModule,
|
|
442
|
-
({
|
|
443
|
-
dropdownProps,
|
|
444
|
-
currentText,
|
|
445
|
-
|
|
446
|
-
module,
|
|
447
|
-
}) => {
|
|
514
|
+
export const Autocomplete = provideModuleToComponent<AutocompleteProps>(AutocompleteModuleBp, ({ inputboxProps, dropdownProps, currentText }) => {
|
|
448
515
|
const service = useInject(AutocompleteService);
|
|
449
516
|
|
|
517
|
+
service.inputboxService.currentValue = currentText;
|
|
518
|
+
|
|
450
519
|
console.log(service.dropdownService.listviewService.items);
|
|
451
520
|
// Produces: [29, 9, 1969]
|
|
452
521
|
|
|
@@ -454,9 +523,9 @@ export const Autocomplete = provideModuleToComponent<AutocompleteProps>(
|
|
|
454
523
|
|
|
455
524
|
return (
|
|
456
525
|
<div className="fancy-autocomplete">
|
|
457
|
-
{/* Let's not forget to
|
|
458
|
-
<Inputbox {...inputboxProps}
|
|
459
|
-
<Dropdown {...dropdownProps}
|
|
526
|
+
{/* Let's not forget to replace the injection providers of both components we want to control */}
|
|
527
|
+
<Inputbox {...inputboxProps} inject={[{ provide: InputboxService, useValue: service.inputboxService }]} >
|
|
528
|
+
<Dropdown {...dropdownProps} inject={[{ provide: DropdownService, useValue: service.dropdownService }]} />
|
|
460
529
|
</div>
|
|
461
530
|
);
|
|
462
531
|
}
|
|
@@ -465,8 +534,6 @@ export const Autocomplete = provideModuleToComponent<AutocompleteProps>(
|
|
|
465
534
|
|
|
466
535
|
This should cover the fundamentals of how you can build a scalable UI by using the `xInjection` Dependency Injection 😊
|
|
467
536
|
|
|
468
|
-
> **Note:** _Keep in mind that both library ([xInjection](https://www.npmjs.com/package/@adimm/x-injection) & [xInjection ReactJS](https://www.npmjs.com/package/@adimm/x-injection-reactjs)) are still young and being developed, therefore the internals and public API may change in the near future._
|
|
469
|
-
|
|
470
537
|
## Unit Tests
|
|
471
538
|
|
|
472
539
|
It is very easy to create mock modules so you can provide them to your components in your unit tests.
|
|
@@ -483,13 +550,14 @@ class ApiService {
|
|
|
483
550
|
private async sendToLocation(user: User, location: any): Promise<any> {}
|
|
484
551
|
}
|
|
485
552
|
|
|
486
|
-
const
|
|
487
|
-
|
|
553
|
+
const ApiModuleBp = new ProviderModule.blueprint({
|
|
554
|
+
id: 'ApiModule',
|
|
488
555
|
providers: [UserService, ApiService],
|
|
489
556
|
});
|
|
490
557
|
|
|
491
|
-
|
|
492
|
-
|
|
558
|
+
// Clone returns a `deep` clone and wraps all the `methods` to break their reference!
|
|
559
|
+
const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
|
|
560
|
+
id: 'ApiModuleMocked',
|
|
493
561
|
providers: [
|
|
494
562
|
{
|
|
495
563
|
provide: UserService,
|
|
@@ -506,8 +574,8 @@ const ApiModuleMocked = new ComponentProviderModule({
|
|
|
506
574
|
],
|
|
507
575
|
});
|
|
508
576
|
|
|
509
|
-
// Now all the dependencies used inside the
|
|
510
|
-
await act(async () => render(<RealComponent module={
|
|
577
|
+
// Now all the dependencies used inside the "RealComponent" will be automatically resolved from the `ApiModuleBpMocked` module.
|
|
578
|
+
await act(async () => render(<RealComponent module={ApiModuleBpMocked} />));
|
|
511
579
|
```
|
|
512
580
|
|
|
513
581
|
## Documentation
|
|
@@ -522,6 +590,12 @@ Pull requests are warmly welcomed! 😃
|
|
|
522
590
|
|
|
523
591
|
Please ensure your contributions adhere to the project's code style. See the repository for more details.
|
|
524
592
|
|
|
593
|
+
## Credits
|
|
594
|
+
|
|
595
|
+
- [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/) - Author of `xInjection` & `xInjection ReactJS`
|
|
596
|
+
|
|
525
597
|
---
|
|
526
598
|
|
|
527
|
-
>
|
|
599
|
+
> [!NOTE]
|
|
600
|
+
>
|
|
601
|
+
> **For questions, feature requests, or bug reports, feel free to open an [issue](https://github.com/AdiMarianMutu/x-injection-react/issues) on GitHub.**
|