@adimm/x-injection-reactjs 1.0.2 → 1.0.3

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 CHANGED
@@ -12,61 +12,53 @@
12
12
  <a href="https://www.npmjs.com/package/@adimm/x-injection-reactjs" target="__blank"><img src="https://badgen.net/npm/dm/@adimm/x-injection-reactjs"></a>
13
13
  </p>
14
14
 
15
+ # xInjection for React
16
+
17
+ Powerful dependency injection for React components using a modular architecture. Build scalable React applications with clean separation of concerns.
18
+
15
19
  ## Table of Contents
16
20
 
17
- - [Table of Contents](#table-of-contents)
18
- - [Overview](#overview)
19
- - [Installation](#installation)
20
- - [TypeScript Configuration](#typescript-configuration)
21
- - [Getting Started](#getting-started)
21
+ - [xInjection for React](#xinjection-for-react)
22
+ - [Table of Contents](#table-of-contents)
23
+ - [Overview](#overview)
24
+ - [Installation](#installation)
22
25
  - [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)
33
- - [Hook Injection](#hook-injection)
34
- - [Examples](#examples)
35
- - [Composable components](#composable-components)
36
- - [Unit Tests](#unit-tests)
37
- - [Documentation](#documentation)
38
- - [Contributing](#contributing)
39
- - [Credits](#credits)
26
+ - [Core Concepts](#core-concepts)
27
+ - [Component Modules](#component-modules)
28
+ - [Services](#services)
29
+ - [Dependency Injection](#dependency-injection)
30
+ - [Custom Hooks](#custom-hooks)
31
+ - [Examples](#examples)
32
+ - [Zustand Integration](#zustand-integration)
33
+ - [Parent-Child Provider Control](#parent-child-provider-control)
34
+ - [Advanced Usage](#advanced-usage)
35
+ - [Module Imports and Exports](#module-imports-and-exports)
36
+ - [Multiple Dependency Injection](#multiple-dependency-injection)
37
+ - [Unit Testing](#unit-testing)
38
+ - [Documentation](#documentation)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
40
41
 
41
42
  ## Overview
42
43
 
43
- This is the _official_ [ReactJS](https://react.dev/) implementation of the [xInjection](https://github.com/AdiMarianMutu/x-injection) library.
44
-
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.
44
+ xInjection for React brings dependency injection to your React components, enabling:
48
45
 
49
- ## Installation
50
-
51
- First, ensure you have [`reflect-metadata`](https://www.npmjs.com/package/reflect-metadata) installed:
46
+ - **Service-based architecture**: Separate business logic from UI components
47
+ - **Modular design**: Create reusable, testable component modules
48
+ - **State management integration**: Works seamlessly with Zustand, Redux, or any state library
49
+ - **Parent-child provider control**: Parent components can control child component dependencies
52
50
 
53
- ```sh
54
- npm i reflect-metadata
55
- ```
51
+ This is the official [ReactJS](https://react.dev/) implementation of [xInjection](https://github.com/AdiMarianMutu/x-injection).
56
52
 
57
- Then install `xInjection` for React:
53
+ ## Installation
58
54
 
59
55
  ```sh
60
- npm i @adimm/x-injection-reactjs
56
+ npm i @adimm/x-injection-reactjs reflect-metadata
61
57
  ```
62
58
 
63
- > [!Note]
64
- >
65
- > You may also have to install the parent library via `npm i @adimm/x-injection`
66
-
67
- ### TypeScript Configuration
59
+ **TypeScript Configuration**
68
60
 
69
- Add the following options to your `tsconfig.json` to enable decorator metadata:
61
+ Add to your `tsconfig.json`:
70
62
 
71
63
  ```json
72
64
  {
@@ -77,17 +69,25 @@ Add the following options to your `tsconfig.json` to enable decorator metadata:
77
69
  }
78
70
  ```
79
71
 
80
- ## Getting Started
81
-
82
- ### Quick Start
72
+ ## Quick Start
83
73
 
84
74
  ```tsx
75
+ import { Injectable, provideModuleToComponent, ProviderModule, useInject } from '@adimm/x-injection-reactjs';
76
+
77
+ // 1. Define a service
78
+ @Injectable()
79
+ class UserService {
80
+ firstName = 'John';
81
+ lastName = 'Doe';
82
+ }
83
+
84
+ // 2. Create a module blueprint
85
85
  const UserDashboardModuleBp = ProviderModule.blueprint({
86
- id: 'ComponentUserDashboardModule',
87
- imports: [UserModule],
88
- exports: [UserModule],
86
+ id: 'UserDashboardModule',
87
+ providers: [UserService],
89
88
  });
90
89
 
90
+ // 3. Create a component with dependency injection
91
91
  const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, () => {
92
92
  const userService = useInject(UserService);
93
93
 
@@ -97,505 +97,327 @@ const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, () => {
97
97
  </h1>
98
98
  );
99
99
  });
100
-
101
- const App = () => {
102
- return (
103
- <>
104
- <Navbar />
105
- <UserDashboard />
106
- <Footer />
107
- </>
108
- );
109
- };
110
100
  ```
111
101
 
112
- ### Conventions
113
-
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.
102
+ ## Core Concepts
115
103
 
116
- ### Component Module
104
+ ### Component Modules
117
105
 
118
- You should create a separate file which you'll use to declare the `blueprint` of the _component_ `module`:
119
-
120
- `user-dashboard/user-dashboard.module.ts`
106
+ Create a module blueprint that defines your component's dependencies:
121
107
 
122
108
  ```ts
109
+ // user-dashboard.module.ts
123
110
  export const UserDashboardModuleBp = ProviderModule.blueprint({
124
- id: 'ComponentUserDashboardModule',
125
- ...
111
+ id: 'UserDashboardModule',
112
+ providers: [UserService],
113
+ exports: [UserService],
126
114
  });
127
115
  ```
128
116
 
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.
132
-
133
- ### Component Service
117
+ **Blueprint vs Module:** Use a **blueprint** for reusable components (multiple instances), use a raw **module** for singleton components (single instance).
134
118
 
135
- You should create a separate file which you'll use to declare the _(main)_ `service` of the _component_.
119
+ ### Services
136
120
 
137
- `user-dashboard/user-dashboard.service.ts`
121
+ Define services using the `@Injectable()` decorator:
138
122
 
139
123
  ```ts
124
+ // user-dashboard.service.ts
140
125
  @Injectable()
141
126
  export class UserDashboardService {
142
127
  firstName: string;
143
128
  lastName: string;
129
+
130
+ getFullName() {
131
+ return `${this.firstName} ${this.lastName}`;
132
+ }
144
133
  }
145
134
  ```
146
135
 
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?
136
+ ### Dependency Injection
152
137
 
153
- > Will you have **more** than **one** instance of that component?
154
-
155
- ##### Yes
156
-
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).
158
-
159
- ##### No
160
-
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`.
138
+ Use `useInject` to access services in your components:
172
139
 
173
140
  ```tsx
174
- const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, (props: UserDashboardProps) => {
175
- ...
176
- });
177
- ```
178
-
179
- ### How to control a Child component providers from Parent component?
180
-
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],
192
- });
193
- ```
194
-
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
- });
203
- ```
204
-
205
- Then, when you are rendering the `Child` component from **within** the `Parent` component:
206
-
207
- ```ts
208
- const ParentComponent = provideModuleToComponent(ParentModuleBp, ({ module }) => {
209
- // the `module` prop is always available and automatically injected into the `props` object.
210
-
211
- return <ChildComponent module={module} />;
212
- });
213
- ```
214
-
215
- Now the `ChildComponent` will be instantiated with the `module` received from the `ParentComponent`, therefore it'll use the `ChildService` managed into the `ParentModule`.
216
-
217
- > [!Tip]
218
- >
219
- > This is perfect to use when you are writing _unit tests_ and you want to mock an entire component `module`
220
-
221
- #### Override only specific Child Providers
222
-
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
- });
233
- ```
234
-
235
- And now the rendering part:
236
-
237
- ```ts
238
- const ParentComponent = provideModuleToComponent(ParentModuleBp, () => {
239
- // notice that we are not using the `module` prop anymore.
240
- const childService = useInject(ChildService);
141
+ const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, () => {
142
+ const userService = useInject(UserService);
241
143
 
242
- return <ChildComponent inject={[{ provide: ChildService, useValue: childService }]} />;
144
+ return <div>{userService.getFullName()}</div>;
243
145
  });
244
146
  ```
245
147
 
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`.
247
-
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.
251
-
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.
148
+ ### Custom Hooks
255
149
 
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.
257
-
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.
150
+ Create reusable hooks with dependency injection using `hookFactory`:
259
151
 
260
152
  ```ts
261
- // The `HookWithDeps` generic type will help
262
- // in making sure that the `useGenerateUserFullName` hooks params are correctly visible.
263
- // The 1st generic param must be the hook params (Like `UserInfoProps`)
264
- // and the 2nd generic param must be an `array` with the providers type.
265
- const useGenerateUserFullName = hookFactory({
266
- // The `use` property is where you write your hook implementation.
267
- use: ({ firstName, lastName, deps: [userService] }: HookWithDeps<UserInfoProps, [UserService]>) => {
153
+ const useUserFullName = hookFactory({
154
+ use: ({ firstName, lastName, deps: [userService] }) => {
268
155
  userService.firstName = firstName;
269
156
  userService.lastName = lastName;
270
-
271
- return userService.generateFullName();
157
+ return userService.getFullName();
272
158
  },
273
- // The `inject` array is very important,
274
- // here we basically specify which dependencies should be injected into the custom hook.
275
- // Also, keep in mind that the order of the `inject` array matters, the order of the `deps` prop
276
- // is determined by the order of the `inject` array!
277
159
  inject: [UserService],
278
160
  });
279
- ```
280
-
281
- Now you can use it in inside any component which is using a `module` which can provide the `UserService`.
282
-
283
- ```tsx
284
- export function UserInfo({ firstName, lastName }: UserInfoProps) {
285
- const userFullName = useGenerateFullName({ firstName, lastName });
286
161
 
287
- return <p>Hello {userFullName}!</p>;
288
- }
162
+ // Use in any component
163
+ const fullName = useUserFullName({ firstName: 'John', lastName: 'Doe' });
289
164
  ```
290
165
 
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
-
295
166
  ## Examples
296
167
 
297
- ### Composable components
298
-
299
- In a real world scenario, you'll definitely have custom components which render other custom components and so on... _(like a [Matryoshka doll](https://en.wikipedia.org/wiki/Matryoshka_doll))_
168
+ ### Zustand Integration
300
169
 
301
- So you may find yourself wanting to be able to control a dependency/service of a child component from a parent component, with `xInjection` this is very easy to achieve thanks to the `ProviderModule` architecture, because each `module` can `import` and `export` other dependencies _(or modules)_ it fits in perfectly within the [declarative programming](https://en.wikipedia.org/wiki/Declarative_programming) world!
170
+ This example shows how to integrate Zustand store within a service, allowing the service to manipulate the store while components only subscribe to state changes.
302
171
 
303
- In this example, we'll build 4 components, each with its own purpose. However, the `autocomplete` component will be the one capable of accessing the services of all of them.
304
-
305
- - An `inputbox`
306
- - A `list viewer`
307
- - A `dropdown`
308
- - An `autocomplete`
309
-
310
- <hr>
172
+ ```ts
173
+ // counter.service.ts
311
174
 
312
- > Inputbox
175
+ import { Injectable } from '@adimm/x-injection-reactjs';
176
+ import { create } from 'zustand';
313
177
 
314
- `inputbox.service.ts`
178
+ interface CounterStore {
179
+ count: number;
180
+ increment: () => void;
181
+ decrement: () => void;
182
+ reset: () => void;
183
+ }
315
184
 
316
- ```ts
317
185
  @Injectable()
318
- export class InputboxService {
319
- currentValue = '';
186
+ export class CounterService {
187
+ // Store instance encapsulated within the service
188
+ private readonly store = create<CounterStore>((set) => ({
189
+ count: 0,
190
+ increment: () => set((state) => ({ count: state.count + 1 })),
191
+ decrement: () => set((state) => ({ count: state.count - 1 })),
192
+ reset: () => set({ count: 0 }),
193
+ }));
194
+
195
+ // Expose store hook for components to subscribe
196
+ get useStore() {
197
+ return this.store;
198
+ }
320
199
 
321
- // We'll initialize this soon enough.
322
- setStateValue!: (newValue: string) => void;
200
+ // Getter to access current state from within the service
201
+ private get storeState() {
202
+ return this.store.getState();
203
+ }
323
204
 
324
- /** Can be used to update the {@link currentValue} of the `inputbox`. */
325
- setValue(newValue: string): void {
326
- this.currentValue = newValue;
205
+ // Business logic methods
206
+ increment() {
207
+ this.storeState.increment();
208
+ }
327
209
 
328
- this.setStateValue(this.currentValue);
210
+ decrement() {
211
+ this.storeState.decrement();
329
212
  }
330
- }
331
213
 
332
- export const InputboxModuleBp = ProviderModule.blueprint({
333
- id: 'ComponentInputboxModule',
334
- provides: [InputboxService],
335
- exports: [InputboxService],
336
- });
337
- ```
214
+ reset() {
215
+ this.storeState.reset();
216
+ }
338
217
 
339
- `inputbox.tsx`
218
+ incrementBy(amount: number) {
219
+ // Complex logic lives in the service
220
+ const currentCount = this.storeState.count;
221
+ this.store.setState({ count: currentCount + amount });
222
+ }
340
223
 
341
- ```tsx
342
- export interface InputboxProps {
343
- initialValue: string;
224
+ async incrementAsync() {
225
+ // Handle async operations in the service
226
+ await new Promise((resolve) => setTimeout(resolve, 1000));
227
+ this.increment();
228
+ }
344
229
  }
345
-
346
- export const Inputbox = provideModuleToComponent<InputboxProps>(InputboxModuleBp, ({ initialValue }) => {
347
- const service = useInject(InputboxService);
348
- const [, setCurrentValue] = useState(initialValue);
349
- service.setStateValue = setCurrentValue;
350
-
351
- useEffect(() => {
352
- service.currentValue = initialValue;
353
- }, [initialValue]);
354
-
355
- return <input value={service.currentValue} onChange={(e) => service.setValue(e.currentTarget.value)} />;
356
- });
357
230
  ```
358
231
 
359
- <hr>
360
-
361
- > Listview
362
-
363
- `listview.service.ts`
364
-
365
232
  ```ts
366
- @Injectable()
367
- export class ListviewService {
368
- items = [];
233
+ // counter.module.ts
369
234
 
370
- /* Remaining fancy implementation */
371
- }
235
+ import { ProviderModule } from '@adimm/x-injection-reactjs';
372
236
 
373
- export const ListviewModuleBp = ProviderModule.blueprint({
374
- id: 'ComponentListviewModule',
375
- provides: [ListviewService],
376
- exports: [ListviewService],
237
+ import { CounterService } from './counter.service';
238
+
239
+ export const CounterModuleBp = ProviderModule.blueprint({
240
+ id: 'CounterModule',
241
+ providers: [CounterService],
242
+ exports: [CounterService],
377
243
  });
378
244
  ```
379
245
 
380
- `listview.tsx`
381
-
382
246
  ```tsx
383
- export interface ListviewProps {
384
- items: any[];
385
- }
247
+ // counter.component.tsx
386
248
 
387
- export const Listview = provideModuleToComponent<ListviewProps>(ListviewModuleBp, ({ items }) => {
388
- const service = useInject(ListviewService);
249
+ import { provideModuleToComponent, useInject } from '@adimm/x-injection-reactjs';
389
250
 
390
- /* Remaining fancy implementation */
251
+ import { CounterModuleBp } from './counter.module';
252
+ import { CounterService } from './counter.service';
253
+
254
+ const Counter = provideModuleToComponent(CounterModuleBp, () => {
255
+ // Inject service for business logic
256
+ const counterService = useInject(CounterService);
257
+
258
+ // Subscribe to store for reactive state
259
+ const count = counterService.useStore((state) => state.count);
391
260
 
392
261
  return (
393
262
  <div>
394
- {service.items.map((item) => (
395
- <span key={item}>{item}</span>
396
- ))}
263
+ <h2>Count: {count}</h2>
264
+ <button onClick={() => counterService.increment()}>+1</button>
265
+ <button onClick={() => counterService.decrement()}>-1</button>
266
+ <button onClick={() => counterService.incrementBy(5)}>+5</button>
267
+ <button onClick={() => counterService.incrementAsync()}>+1 Async</button>
268
+ <button onClick={() => counterService.reset()}>Reset</button>
397
269
  </div>
398
270
  );
399
271
  });
272
+
273
+ export default Counter;
400
274
  ```
401
275
 
402
- <hr>
276
+ **Key Benefits:**
403
277
 
404
- > Dropdown
278
+ - **Encapsulation**: Store is encapsulated within the service, not exposed globally
279
+ - **Separation of concerns**: Business logic in services, UI only subscribes to state
280
+ - **Testability**: Services are self-contained and easy to test
281
+ - **Reusability**: Services with stores can be shared across components via dependency injection
282
+ - **Type safety**: Full TypeScript support throughout
405
283
 
406
- Now keep close attention to how we implement the `Dropdown` component, as it'll actually be the _parent_ controlling the `Listview` component own service.
284
+ ### Parent-Child Provider Control
407
285
 
408
- `dropdown.service.ts`
286
+ Parent components can control child component dependencies using the `inject` prop:
409
287
 
410
288
  ```ts
411
- @Injectable()
412
- export class DropdownService {
413
- constructor(readonly listviewService: ListviewService) {
414
- // We can already take control of the children `ListviewService`!
415
- this.listviewService.items = [1, 2, 3, 4, 5];
416
- }
417
-
418
- /* Remaining fancy implementation */
419
- }
420
-
421
- export const DropdownModuleBp = ProviderModule.blueprint({
422
- id: 'ComponentDropdownModule',
423
- // It is very important that we import all the exportable dependencies from the `ListviewModule`!
424
- imports: [ListviewModuleBp],
425
- provides: [DropdownService],
426
- exports: [
427
- // Let's also re-export the dependencies of the `ListviewModule` so once we import the `DropdownModule`
428
- // somewhere elese, we get access to the `ListviewModule` exported dependencies as well!
429
- ListviewModuleBp,
430
- // Let's not forget to also export our `DropdownService` :)
431
- DropdownService,
432
- ],
289
+ // Child module and service
290
+ const ChildModuleBp = ProviderModule.blueprint({
291
+ id: 'ChildModule',
292
+ providers: [ChildService],
293
+ exports: [ChildService],
433
294
  });
434
- ```
435
-
436
- `dropdown.tsx`
437
-
438
- ```tsx
439
- export interface DropdownProps {
440
- listviewProps: ListviewProps;
441
295
 
442
- initialSelectedValue: number;
443
- }
444
-
445
- export const Dropdown = provideModuleToComponent<DropdownProps>(
446
- ListviewModuleBp,
447
- ({ listviewProps, initialSelectedValue }) => {
448
- const service = useInject(DropdownService);
449
-
450
- /* Remaining fancy implementation */
296
+ // Parent module
297
+ const ParentModuleBp = ProviderModule.blueprint({
298
+ id: 'ParentModule',
299
+ providers: [ParentService, ChildService],
300
+ exports: [ParentService, ChildService],
301
+ });
451
302
 
452
- return (
453
- <div className="fancy-dropdown">
454
- <span>{initialSelectedValue}</span>
303
+ // Parent component controls child's service
304
+ const ParentComponent = provideModuleToComponent(ParentModuleBp, () => {
305
+ const childService = useInject(ChildService);
455
306
 
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 }]} />
459
- </div>
460
- );
461
- }
462
- );
307
+ // Override child's ChildService with parent's instance
308
+ return <ChildComponent inject={[{ provide: ChildService, useValue: childService }]} />;
309
+ });
463
310
  ```
464
311
 
465
- <hr>
312
+ This pattern is useful for:
466
313
 
467
- > Autocomplete
314
+ - Building composable component hierarchies
315
+ - Sharing state between parent and child components
316
+ - Creating flexible component APIs
468
317
 
469
- And finally the grand finale!
318
+ ## Advanced Usage
470
319
 
471
- `autocomplete.service.ts`
320
+ ### Module Imports and Exports
472
321
 
473
- ```ts
474
- @Injectable()
475
- export class AutocompleteService {
476
- constructor(
477
- readonly inputboxService: InputboxService,
478
- readonly dropdownService: DropdownService
479
- ) {
480
- // Here we can override even what the `Dropdown` has already overriden!
481
- this.dropdownService.listviewService.items = [29, 9, 1969];
482
-
483
- // However doing the following, will throw an error because the `Inputbox` component
484
- // at this time is not yet mounted, therefore the `setStateValue` state setter
485
- // method doesn't exist yet.
486
- //
487
- // A better way would be to use a store manager so you can generate your application state through
488
- // the services, rather than inside the UI (components should be used only to render the data, not to manipulate/manage it).
489
- this.inputboxService.setValue('xInjection');
490
- }
322
+ Modules can import and re-export other modules:
491
323
 
492
- /* Remaining fancy implementation */
493
- }
494
-
495
- export const AutocompleteModuleBp = ProviderModule.blueprint({
496
- id: 'ComponentAutocompleteModule',
497
- imports: [InputboxModuleBp, DropdownModuleBp],
498
- provides: [AutocompleteService],
499
- // If we don't plan to share the internal dependencies of the
500
- // Autocomplete component, then we can omit the `exports` array declaration.
324
+ ```ts
325
+ const DropdownModuleBp = ProviderModule.blueprint({
326
+ id: 'DropdownModule',
327
+ imports: [ListviewModuleBp], // Import ListviewModule
328
+ providers: [DropdownService],
329
+ exports: [
330
+ ListviewModuleBp, // Re-export imported module
331
+ DropdownService,
332
+ ],
501
333
  });
502
334
  ```
503
335
 
504
- `autocomplete.tsx`
505
-
506
- ```tsx
507
- export interface AutocompleteProps {
508
- inputboxProps: InputboxProps;
509
- dropdownProps: DropdownProps;
510
-
511
- currentText: string;
512
- }
513
-
514
- export const Autocomplete = provideModuleToComponent<AutocompleteProps>(AutocompleteModuleBp, ({ inputboxProps, dropdownProps, currentText }) => {
515
- const service = useInject(AutocompleteService);
516
-
517
- service.inputboxService.currentValue = currentText;
336
+ ### Multiple Dependency Injection
518
337
 
519
- console.log(service.dropdownService.listviewService.items);
520
- // Produces: [29, 9, 1969]
338
+ Use `useInjectMany` to inject multiple dependencies:
521
339
 
522
- /* Remaining fancy implementation */
523
-
524
- return (
525
- <div className="fancy-autocomplete">
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 }]} />
529
- </div>
530
- );
531
- }
532
- );
340
+ ```ts
341
+ const [userService, apiService] = useInjectMany([UserService, ApiService]);
533
342
  ```
534
343
 
535
- This should cover the fundamentals of how you can build a scalable UI by using the `xInjection` Dependency Injection 😊
344
+ ## Unit Testing
536
345
 
537
- ## Unit Tests
538
-
539
- It is very easy to create mock modules so you can provide them to your components in your unit tests.
346
+ Mock modules easily for testing:
540
347
 
541
348
  ```tsx
542
- class ApiService {
543
- constructor(private readonly userService: UserService) {}
544
-
545
- async sendRequest<T>(location: LocationParams): Promise<T> {
546
- // Pseudo Implementation
547
- return this.sendToLocation(user, location);
548
- }
349
+ import { act, render } from '@testing-library/react';
549
350
 
550
- private async sendToLocation(user: User, location: any): Promise<any> {}
551
- }
552
-
553
- const ApiModuleBp = new ProviderModule.blueprint({
351
+ // Original module
352
+ const ApiModuleBp = ProviderModule.blueprint({
554
353
  id: 'ApiModule',
555
354
  providers: [UserService, ApiService],
556
355
  });
557
356
 
558
- // Clone returns a `deep` clone and wraps all the `methods` to break their reference!
357
+ // Create mocked version
559
358
  const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
560
359
  id: 'ApiModuleMocked',
561
360
  providers: [
562
- {
563
- provide: UserService,
564
- useClass: UserService_Mock,
565
- },
361
+ { provide: UserService, useClass: UserServiceMock },
566
362
  {
567
363
  provide: ApiService,
568
364
  useValue: {
569
- sendRequest: async (location) => {
570
- console.log(location);
571
- },
365
+ sendRequest: vi.fn().mockResolvedValue({ data: 'mocked' }),
572
366
  },
573
367
  },
574
368
  ],
575
369
  });
576
370
 
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} />));
371
+ // Test with mocked module
372
+ await act(async () => render(<MyComponent module={ApiModuleBpMocked} />));
373
+ ```
374
+
375
+ **Testing with Zustand:**
376
+
377
+ ```tsx
378
+ import { act, renderHook } from '@testing-library/react';
379
+
380
+ import { CounterService } from './counter.service';
381
+
382
+ it('should increment counter via service', () => {
383
+ const service = new CounterService();
384
+
385
+ const { result } = renderHook(() => service.useStore((s) => s.count));
386
+
387
+ expect(result.current).toBe(0);
388
+
389
+ act(() => {
390
+ service.increment();
391
+ });
392
+
393
+ expect(result.current).toBe(1);
394
+ });
395
+
396
+ it('should handle complex business logic', () => {
397
+ const service = new CounterService();
398
+
399
+ act(() => {
400
+ service.incrementBy(10);
401
+ });
402
+
403
+ expect(service.useStore.getState().count).toBe(10);
404
+ });
579
405
  ```
580
406
 
581
407
  ## Documentation
582
408
 
583
- Comprehensive, auto-generated documentation is available at:
409
+ 📚 **Full API Documentation:** [https://adimarianmutu.github.io/x-injection-reactjs](https://adimarianmutu.github.io/x-injection-reactjs/index.html)
584
410
 
585
- 👉 [https://adimarianmutu.github.io/x-injection-reactjs/index.html](https://adimarianmutu.github.io/x-injection-reactjs/index.html)
411
+ For more information about the base library, see [xInjection Documentation](https://github.com/AdiMarianMutu/x-injection#readme).
586
412
 
587
413
  ## Contributing
588
414
 
589
- Pull requests are warmly welcomed! 😃
590
-
591
- Please ensure your contributions adhere to the project's code style. See the repository for more details.
415
+ Pull requests are welcome! Please ensure your contributions follow the project's code style.
592
416
 
593
- ## Credits
417
+ ## License
594
418
 
595
- - [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/) - Author of `xInjection` & `xInjection ReactJS`
419
+ MIT © [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/)
596
420
 
597
421
  ---
598
422
 
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.**
423
+ **Questions or issues?** Open an [issue on GitHub](https://github.com/AdiMarianMutu/x-injection-reactjs/issues)
package/dist/index.cjs CHANGED
@@ -9,91 +9,92 @@ var e, r = Object.create, t = Object.defineProperty, o = Object.getOwnPropertyDe
9
9
  enumerable: !(c = o(r, a)) || c.enumerable
10
10
  });
11
11
  return e;
12
- }, d = {};
12
+ }, s = {};
13
13
 
14
14
  ((e, r) => {
15
15
  for (var o in r) t(e, o, {
16
16
  get: r[o],
17
17
  enumerable: !0
18
18
  });
19
- })(d, {
19
+ })(s, {
20
20
  REACT_X_INJECTION_PROVIDER_MODULE_CONTEXT: () => l,
21
- hookFactory: () => _,
21
+ hookFactory: () => q,
22
22
  provideModuleToComponent: () => T,
23
- useComponentModule: () => m,
24
- useInject: () => f,
23
+ useComponentModule: () => f,
24
+ useInject: () => m,
25
25
  useInjectMany: () => v
26
- }), module.exports = (e = d, a(t({}, "__esModule", {
26
+ }), module.exports = (e = s, a(t({}, "__esModule", {
27
27
  value: !0
28
28
  }), e));
29
29
 
30
- var s = require("@adimm/x-injection"), l = (0, require("react").createContext)(s.AppModule), p = require("react");
30
+ var d = require("@adimm/x-injection"), l = (0, require("react").createContext)(d.AppModule), p = require("react");
31
31
 
32
- function m() {
32
+ function f() {
33
33
  return (0, p.useContext)(l);
34
34
  }
35
35
 
36
- function f(e, r) {
37
- return m().get(e, r?.isOptional, r?.asList);
36
+ function m(e, r) {
37
+ return f().get(e, r?.isOptional, r?.asList);
38
38
  }
39
39
 
40
40
  function v(...e) {
41
- return m().getMany(...e);
41
+ return f().getMany(...e);
42
42
  }
43
43
 
44
- c(m, "useComponentModule"), c(f, "useInject"), c(v, "useInjectMany");
44
+ c(f, "useComponentModule"), c(m, "useInject"), c(v, "useInjectMany");
45
45
 
46
- var M = ((e, o, n) => (n = null != e ? r(u(e)) : {}, a(!o && e && e.__esModule ? n : t(n, "default", {
46
+ var M, y = ((e, o, n) => (n = null != e ? r(u(e)) : {}, a(!o && e && e.__esModule ? n : t(n, "default", {
47
47
  value: e,
48
48
  enumerable: !0
49
- }), e)))(require("react"), 1), j = require("@adimm/x-injection"), P = require("react"), y = require("react");
49
+ }), e)))(require("react"), 1), j = require("@adimm/x-injection"), P = require("react"), b = require("react");
50
50
 
51
- function b(e) {
52
- const r = (0, y.useRef)(void 0), t = (0, y.useRef)(!1), o = (0, y.useRef)(!1), [, n] = (0,
53
- y.useState)(0);
54
- t.current && (o.current = !0), (0, y.useEffect)(() => (t.current || (r.current = e(),
55
- t.current = !0), n(e => e + 1), () => {
51
+ function h(e) {
52
+ const r = (0, b.useRef)(void 0), t = (0, b.useRef)(!1), o = (0, b.useRef)(!1), [, n] = (0,
53
+ b.useState)(0);
54
+ t.current && (o.current = !0), (0, b.useEffect)((() => (t.current || (r.current = e(),
55
+ t.current = !0), n((e => e + 1)), () => {
56
56
  o.current && r.current?.();
57
- }), []);
57
+ })), []);
58
58
  }
59
59
 
60
60
  function O(e, r) {
61
- const t = (0, P.useMemo)(() => {
61
+ const t = (0, P.useMemo)((() => {
62
62
  const {module: t, inject: o} = r ?? {}, n = j.ProviderModuleHelpers.tryBlueprintToModule(t ?? e);
63
63
  if (n.options.isGlobal) throw new j.InjectionProviderModuleError(n, "A 'global' module can't be supplied to a component!");
64
- return o?.forEach(e => {
64
+ return o?.forEach((e => {
65
65
  if (!n.hasProvider(e)) throw new j.InjectionProviderModuleError(n, `The [${j.ProviderTokenHelpers.providerTokenToString(e)}] provider can't be replaced because it is not part of the component module!`);
66
66
  n.update.removeProvider(j.ProviderTokenHelpers.toProviderIdentifier(e)), n.update.addProvider(e);
67
- }), n;
68
- }, [ e, r?.inject ]);
69
- return b(() => () => {
67
+ })), n;
68
+ }), [ e, r?.inject ]);
69
+ return h((() => () => {
70
70
  t.dispose();
71
- }), t;
71
+ })), t;
72
72
  }
73
73
 
74
- c(b, "useEffectOnce"), c(O, "useMakeOrGetComponentModule");
75
-
76
- var h, E = require("@adimm/x-injection");
74
+ function g(e) {
75
+ return "function" == typeof e && !Function.prototype.toString.call(e).startsWith("class ");
76
+ }
77
77
 
78
- !function(e) {
78
+ c(h, "useEffectOnce"), c(O, "useMakeOrGetComponentModule"), c(g, "isFunction"),
79
+ function(e) {
79
80
  function r(e, r, t) {
80
81
  const o = {
81
82
  ...r
82
83
  };
83
- return ("object" == typeof e && "type" in e && (0, E.isFunction)(e.type) || (0,
84
- E.isFunction)(e)) && (o.module = t), o;
84
+ return ("object" == typeof e && "type" in e && g(e.type) || g(e)) && (o.module = t),
85
+ o;
85
86
  }
86
87
  c(r, "forwardPropsWithModule"), e.forwardPropsWithModule = r;
87
- }(h || (h = {}));
88
+ }(M || (M = {}));
88
89
 
89
- var g = M.default.memo(w);
90
+ var E = y.default.memo(w);
90
91
 
91
92
  function T(e, r) {
92
93
  return t => {
93
94
  const o = O(e, t);
94
- return M.default.createElement(l.Provider, {
95
+ return y.default.createElement(l.Provider, {
95
96
  value: o
96
- }, M.default.createElement(g, {
97
+ }, y.default.createElement(E, {
97
98
  module: o,
98
99
  componentProps: t,
99
100
  component: r
@@ -102,24 +103,24 @@ function T(e, r) {
102
103
  }
103
104
 
104
105
  function w({module: e, component: r, componentProps: t}) {
105
- return M.default.createElement(M.default.Fragment, null, r(h.forwardPropsWithModule(r, t, e)));
106
+ return y.default.createElement(y.default.Fragment, null, r(M.forwardPropsWithModule(r, t, e)));
106
107
  }
107
108
 
108
109
  c(T, "provideModuleToComponent"), c(w, "_ComponentRenderer");
109
110
 
110
- var I = require("react"), C = require("@adimm/x-injection"), q = class e extends C.InjectionProviderModuleError {
111
+ var I = require("react"), C = require("@adimm/x-injection"), _ = class e extends C.InjectionProviderModuleError {
111
112
  static {
112
113
  c(this, "InjectionHookFactoryError");
113
114
  }
114
115
  name=e.name;
115
116
  };
116
117
 
117
- function _({use: e, inject: r}) {
118
+ function q({use: e, inject: r}) {
118
119
  return t => {
119
- const o = m(), n = (0, I.useMemo)(() => {
120
- if (0 === r.length) throw new q(o, "The 'deps' property array is missing!");
120
+ const o = f(), n = (0, I.useMemo)((() => {
121
+ if (0 === r.length) throw new _(o, "The 'deps' property array is missing!");
121
122
  return o.getMany(...r);
122
- }, [ r ]);
123
+ }), [ r ]);
123
124
  return e({
124
125
  ...t,
125
126
  deps: [ ...n ]
@@ -127,4 +128,4 @@ function _({use: e, inject: r}) {
127
128
  };
128
129
  }
129
130
 
130
- c(_, "hookFactory");
131
+ c(q, "hookFactory");
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- var e = Object.defineProperty, r = (r, o) => e(r, "name", {
2
- value: o,
1
+ var e = Object.defineProperty, o = (o, r) => e(o, "name", {
2
+ value: r,
3
3
  configurable: !0
4
4
  });
5
5
 
6
- import { AppModule as o } from "@adimm/x-injection";
6
+ import { AppModule as r } from "@adimm/x-injection";
7
7
 
8
8
  import { createContext as t } from "react";
9
9
 
10
- var n, i = t(o);
10
+ var n, i = t(r);
11
11
 
12
12
  import { useContext as c } from "react";
13
13
 
@@ -15,106 +15,107 @@ function u() {
15
15
  return c(i);
16
16
  }
17
17
 
18
- function m(e, r) {
19
- return u().get(e, r?.isOptional, r?.asList);
18
+ function a(e, o) {
19
+ return u().get(e, o?.isOptional, o?.asList);
20
20
  }
21
21
 
22
- function a(...e) {
22
+ function m(...e) {
23
23
  return u().getMany(...e);
24
24
  }
25
25
 
26
- r(u, "useComponentModule"), r(m, "useInject"), r(a, "useInjectMany");
26
+ o(u, "useComponentModule"), o(a, "useInject"), o(m, "useInjectMany");
27
27
 
28
28
  import p from "react";
29
29
 
30
- import { InjectionProviderModuleError as d, ProviderModuleHelpers as s, ProviderTokenHelpers as f } from "@adimm/x-injection";
30
+ import { InjectionProviderModuleError as s, ProviderModuleHelpers as d, ProviderTokenHelpers as f } from "@adimm/x-injection";
31
31
 
32
32
  import { useMemo as l } from "react";
33
33
 
34
- import { useEffect as v, useRef as h, useState as j } from "react";
34
+ import { useEffect as v, useRef as h, useState as y } from "react";
35
35
 
36
36
  function M(e) {
37
- const r = h(void 0), o = h(!1), t = h(!1), [, n] = j(0);
38
- o.current && (t.current = !0), v(() => (o.current || (r.current = e(), o.current = !0),
39
- n(e => e + 1), () => {
40
- t.current && r.current?.();
41
- }), []);
37
+ const o = h(void 0), r = h(!1), t = h(!1), [, n] = y(0);
38
+ r.current && (t.current = !0), v((() => (r.current || (o.current = e(), r.current = !0),
39
+ n((e => e + 1)), () => {
40
+ t.current && o.current?.();
41
+ })), []);
42
42
  }
43
43
 
44
- function y(e, r) {
45
- const o = l(() => {
46
- const {module: o, inject: t} = r ?? {}, n = s.tryBlueprintToModule(o ?? e);
47
- if (n.options.isGlobal) throw new d(n, "A 'global' module can't be supplied to a component!");
48
- return t?.forEach(e => {
49
- if (!n.hasProvider(e)) throw new d(n, `The [${f.providerTokenToString(e)}] provider can't be replaced because it is not part of the component module!`);
44
+ function j(e, o) {
45
+ const r = l((() => {
46
+ const {module: r, inject: t} = o ?? {}, n = d.tryBlueprintToModule(r ?? e);
47
+ if (n.options.isGlobal) throw new s(n, "A 'global' module can't be supplied to a component!");
48
+ return t?.forEach((e => {
49
+ if (!n.hasProvider(e)) throw new s(n, `The [${f.providerTokenToString(e)}] provider can't be replaced because it is not part of the component module!`);
50
50
  n.update.removeProvider(f.toProviderIdentifier(e)), n.update.addProvider(e);
51
- }), n;
52
- }, [ e, r?.inject ]);
53
- return M(() => () => {
54
- o.dispose();
55
- }), o;
51
+ })), n;
52
+ }), [ e, o?.inject ]);
53
+ return M((() => () => {
54
+ r.dispose();
55
+ })), r;
56
56
  }
57
57
 
58
- r(M, "useEffectOnce"), r(y, "useMakeOrGetComponentModule");
59
-
60
- import { isFunction as P } from "@adimm/x-injection";
58
+ function P(e) {
59
+ return "function" == typeof e && !Function.prototype.toString.call(e).startsWith("class ");
60
+ }
61
61
 
62
- !function(e) {
63
- function o(e, r, o) {
62
+ o(M, "useEffectOnce"), o(j, "useMakeOrGetComponentModule"), o(P, "isFunction"),
63
+ function(e) {
64
+ function r(e, o, r) {
64
65
  const t = {
65
- ...r
66
+ ...o
66
67
  };
67
- return ("object" == typeof e && "type" in e && P(e.type) || P(e)) && (t.module = o),
68
+ return ("object" == typeof e && "type" in e && P(e.type) || P(e)) && (t.module = r),
68
69
  t;
69
70
  }
70
- r(o, "forwardPropsWithModule"), e.forwardPropsWithModule = o;
71
+ o(r, "forwardPropsWithModule"), e.forwardPropsWithModule = r;
71
72
  }(n || (n = {}));
72
73
 
73
74
  var g = p.memo(b);
74
75
 
75
- function w(e, r) {
76
- return o => {
77
- const t = y(e, o);
76
+ function w(e, o) {
77
+ return r => {
78
+ const t = j(e, r);
78
79
  return p.createElement(i.Provider, {
79
80
  value: t
80
81
  }, p.createElement(g, {
81
82
  module: t,
82
- componentProps: o,
83
- component: r
83
+ componentProps: r,
84
+ component: o
84
85
  }));
85
86
  };
86
87
  }
87
88
 
88
- function b({module: e, component: r, componentProps: o}) {
89
- return p.createElement(p.Fragment, null, r(n.forwardPropsWithModule(r, o, e)));
89
+ function b({module: e, component: o, componentProps: r}) {
90
+ return p.createElement(p.Fragment, null, o(n.forwardPropsWithModule(o, r, e)));
90
91
  }
91
92
 
92
- r(w, "provideModuleToComponent"), r(b, "_ComponentRenderer");
93
+ o(w, "provideModuleToComponent"), o(b, "_ComponentRenderer");
93
94
 
94
95
  import { useMemo as E } from "react";
95
96
 
96
- import { InjectionProviderModuleError as x } from "@adimm/x-injection";
97
+ import { InjectionProviderModuleError as T } from "@adimm/x-injection";
97
98
 
98
- var T = class e extends x {
99
+ var x = class e extends T {
99
100
  static {
100
- r(this, "InjectionHookFactoryError");
101
+ o(this, "InjectionHookFactoryError");
101
102
  }
102
103
  name=e.name;
103
104
  };
104
105
 
105
- function I({use: e, inject: r}) {
106
- return o => {
107
- const t = u(), n = E(() => {
108
- if (0 === r.length) throw new T(t, "The 'deps' property array is missing!");
109
- return t.getMany(...r);
110
- }, [ r ]);
106
+ function F({use: e, inject: o}) {
107
+ return r => {
108
+ const t = u(), n = E((() => {
109
+ if (0 === o.length) throw new x(t, "The 'deps' property array is missing!");
110
+ return t.getMany(...o);
111
+ }), [ o ]);
111
112
  return e({
112
- ...o,
113
+ ...r,
113
114
  deps: [ ...n ]
114
115
  });
115
116
  };
116
117
  }
117
118
 
118
- r(I, "hookFactory");
119
+ o(F, "hookFactory");
119
120
 
120
- export { i as REACT_X_INJECTION_PROVIDER_MODULE_CONTEXT, I as hookFactory, w as provideModuleToComponent, u as useComponentModule, m as useInject, a as useInjectMany };
121
+ export { i as REACT_X_INJECTION_PROVIDER_MODULE_CONTEXT, F as hookFactory, w as provideModuleToComponent, u as useComponentModule, a as useInject, m as useInjectMany };
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@adimm/x-injection-reactjs",
3
+ "private": false,
4
+ "repository": {
5
+ "url": "https://github.com/AdiMarianMutu/x-injection-reactjs"
6
+ },
3
7
  "description": "ReactJS integration of the `xInjection` library.",
4
- "version": "1.0.2",
8
+ "version": "1.0.3",
5
9
  "author": "Adi-Marian Mutu",
6
10
  "homepage": "https://github.com/AdiMarianMutu/x-injection-reactjs#readme",
7
11
  "bugs": "https://github.com/AdiMarianMutu/x-injection-reactjs/issues",
@@ -38,7 +42,7 @@
38
42
  "v:bump-major": "npm version major -m \"chore: update lib major version %s\""
39
43
  },
40
44
  "dependencies": {
41
- "@adimm/x-injection": "^2.1.0",
45
+ "@adimm/x-injection": "^3.0.0",
42
46
  "react": ">=18.0.0"
43
47
  },
44
48
  "devDependencies": {