@adimm/x-injection-reactjs 1.0.2 → 1.0.4

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
+ **Powerful dependency injection for React components using a modular architecture. Build scalable React applications with clean separation of concerns.** _(Inspired by Angular and NestJS IoC/DI)_
16
+
15
17
  ## Table of Contents
16
18
 
17
19
  - [Table of Contents](#table-of-contents)
18
20
  - [Overview](#overview)
19
21
  - [Installation](#installation)
20
- - [TypeScript Configuration](#typescript-configuration)
21
- - [Getting Started](#getting-started)
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)
33
- - [Hook Injection](#hook-injection)
22
+ - [Quick Start](#quick-start)
23
+ - [The Problem](#the-problem)
24
+ - [Without xInjection](#without-xinjection)
25
+ - [With xInjection](#with-xinjection)
26
+ - [Core Concepts](#core-concepts)
27
+ - [Component Modules](#component-modules)
28
+ - [Services](#services)
29
+ - [Dependency Injection](#dependency-injection)
30
+ - [Custom Hooks](#custom-hooks)
34
31
  - [Examples](#examples)
35
- - [Composable components](#composable-components)
36
- - [Unit Tests](#unit-tests)
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)
37
38
  - [Documentation](#documentation)
38
39
  - [Contributing](#contributing)
39
- - [Credits](#credits)
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
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
50
50
 
51
- First, ensure you have [`reflect-metadata`](https://www.npmjs.com/package/reflect-metadata) installed:
51
+ This is the official [ReactJS](https://react.dev/) implementation of [xInjection](https://github.com/AdiMarianMutu/x-injection).
52
52
 
53
- ```sh
54
- npm i reflect-metadata
55
- ```
56
-
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`
59
+ **TypeScript Configuration**
66
60
 
67
- ### TypeScript Configuration
68
-
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,388 @@ 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.
115
-
116
- ### Component Module
117
-
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`
121
-
122
- ```ts
123
- export const UserDashboardModuleBp = ProviderModule.blueprint({
124
- id: 'ComponentUserDashboardModule',
125
- ...
126
- });
127
- ```
102
+ ## The Problem
128
103
 
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.
104
+ React apps often suffer from **provider hell**, **prop drilling**, and **manual dependency wiring**:
132
105
 
133
- ### Component Service
106
+ ### Without xInjection
134
107
 
135
- You should create a separate file which you'll use to declare the _(main)_ `service` of the _component_.
136
-
137
- `user-dashboard/user-dashboard.service.ts`
138
-
139
- ```ts
140
- @Injectable()
141
- export class UserDashboardService {
142
- firstName: string;
143
- lastName: string;
108
+ ```tsx
109
+ // Problem 1: Provider Hell
110
+ <AuthProvider>
111
+ <ApiProvider>
112
+ <ToastProvider>
113
+ <App />
114
+ </ToastProvider>
115
+ </ApiProvider>
116
+ </AuthProvider>;
117
+
118
+ // Problem 2: Manual Dependency Wiring
119
+ function UserProfile() {
120
+ // Must manually create ALL dependencies in correct order
121
+ const toast = new ToastService();
122
+ const api = new ApiService();
123
+ const auth = new AuthService(api);
124
+ const userProfile = new UserProfileService(api, auth, toast);
125
+
126
+ // If AuthService adds a dependency, ALL consumers break!
127
+ return <div>{userProfile.displayName}</div>;
144
128
  }
145
129
  ```
146
130
 
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
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`.
131
+ ### With xInjection
172
132
 
173
133
  ```tsx
174
- const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, (props: UserDashboardProps) => {
175
- ...
134
+ // 1. Define global services (shared across all components) - Usually in your app entrypoint/bootstrap file.
135
+ const AppModuleBp = ProviderModule.blueprint({
136
+ id: 'AppModule',
137
+ isGlobal: true, // Available everywhere, only created once
138
+ providers: [ToastService, ApiService, AuthService],
176
139
  });
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
140
 
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],
141
+ // 2. Define component-specific services - Per component
142
+ const UserProfileModuleBp = ProviderModule.blueprint({
143
+ id: 'UserProfileModule',
144
+ providers: [UserProfileService], // Automatically gets ApiService, AuthService, ToastService
202
145
  });
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
146
 
211
- return <ChildComponent module={module} />;
147
+ const UserProfile = provideModuleToComponent(UserProfileModuleBp, () => {
148
+ const userProfile = useInject(UserProfileService);
149
+ // IoC automatically injects: ToastService → ApiService → AuthService → UserProfileService
150
+ return <div>{userProfile.displayName}</div>;
212
151
  });
213
152
  ```
214
153
 
215
- Now the `ChildComponent` will be instantiated with the `module` received from the `ParentComponent`, therefore it'll use the `ChildService` managed into the `ParentModule`.
154
+ **What You Get:**
216
155
 
217
- > [!Tip]
218
- >
219
- > This is perfect to use when you are writing _unit tests_ and you want to mock an entire component `module`
156
+ - **No Provider Hell** - One module replaces nested providers
157
+ - **Auto Dependency Resolution** - IoC wires everything automatically
158
+ - **Easy Refactoring** - Add/remove dependencies without breaking consumers
159
+ - **Clean Separation** - Business logic in services, UI in components
160
+ - **Fully Testable** - Mock modules or individual services
161
+ - **Type-Safe** - Full TypeScript support
220
162
 
221
- #### Override only specific Child Providers
163
+ ## Core Concepts
222
164
 
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.
165
+ ### Component Modules
224
166
 
225
- Let's re-use the same example as the one from the above, the `ParentModule`:
167
+ Create a module blueprint that defines your component's dependencies:
226
168
 
227
169
  ```ts
228
- const ParentModuleBp = ProviderModule.blueprint({
229
- id: 'ComponentParentModule',
230
- providers: [ParentService, ChildService],
231
- exports: [ParentService, ChildService],
170
+ // user-dashboard.module.ts
171
+ export const UserDashboardModuleBp = ProviderModule.blueprint({
172
+ id: 'UserDashboardModule',
173
+ providers: [UserService],
174
+ exports: [UserService],
232
175
  });
233
176
  ```
234
177
 
235
- And now the rendering part:
178
+ **Blueprint vs Module:** Use a **blueprint** for reusable components (multiple instances), use a raw **module** for singleton components (single instance).
179
+
180
+ ### Services
181
+
182
+ Define services using the `@Injectable()` decorator:
236
183
 
237
184
  ```ts
238
- const ParentComponent = provideModuleToComponent(ParentModuleBp, () => {
239
- // notice that we are not using the `module` prop anymore.
240
- const childService = useInject(ChildService);
185
+ // user-dashboard.service.ts
186
+ @Injectable()
187
+ export class UserDashboardService {
188
+ firstName: string;
189
+ lastName: string;
241
190
 
242
- return <ChildComponent inject={[{ provide: ChildService, useValue: childService }]} />;
243
- });
191
+ getFullName() {
192
+ return `${this.firstName} ${this.lastName}`;
193
+ }
194
+ }
244
195
  ```
245
196
 
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`.
197
+ ### Dependency Injection
247
198
 
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.
199
+ Use `useInject` to access services in your components:
251
200
 
252
- ### Hook Injection
201
+ ```tsx
202
+ const UserDashboard = provideModuleToComponent(UserDashboardModuleBp, () => {
203
+ const userService = useInject(UserService);
253
204
 
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.
205
+ return <div>{userService.getFullName()}</div>;
206
+ });
207
+ ```
255
208
 
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.
209
+ ### Custom Hooks
257
210
 
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.
211
+ Create reusable hooks with dependency injection using `hookFactory`:
259
212
 
260
213
  ```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]>) => {
214
+ const useUserFullName = hookFactory({
215
+ use: ({ firstName, lastName, deps: [userService] }) => {
268
216
  userService.firstName = firstName;
269
217
  userService.lastName = lastName;
270
-
271
- return userService.generateFullName();
218
+ return userService.getFullName();
272
219
  },
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
220
  inject: [UserService],
278
221
  });
279
- ```
280
-
281
- Now you can use it in inside any component which is using a `module` which can provide the `UserService`.
282
222
 
283
- ```tsx
284
- export function UserInfo({ firstName, lastName }: UserInfoProps) {
285
- const userFullName = useGenerateFullName({ firstName, lastName });
286
-
287
- return <p>Hello {userFullName}!</p>;
288
- }
223
+ // Use in any component
224
+ const fullName = useUserFullName({ firstName: 'John', lastName: 'Doe' });
289
225
  ```
290
226
 
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
227
  ## Examples
296
228
 
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))_
300
-
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!
229
+ ### Zustand Integration
302
230
 
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.
231
+ 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.
304
232
 
305
- - An `inputbox`
306
- - A `list viewer`
307
- - A `dropdown`
308
- - An `autocomplete`
309
-
310
- <hr>
233
+ ```ts
234
+ // counter.service.ts
311
235
 
312
- > Inputbox
236
+ import { Injectable } from '@adimm/x-injection-reactjs';
237
+ import { create } from 'zustand';
313
238
 
314
- `inputbox.service.ts`
239
+ interface CounterStore {
240
+ count: number;
241
+ increment: () => void;
242
+ decrement: () => void;
243
+ reset: () => void;
244
+ }
315
245
 
316
- ```ts
317
246
  @Injectable()
318
- export class InputboxService {
319
- currentValue = '';
247
+ export class CounterService {
248
+ // Store instance encapsulated within the service
249
+ private readonly store = create<CounterStore>((set) => ({
250
+ count: 0,
251
+ increment: () => set((state) => ({ count: state.count + 1 })),
252
+ decrement: () => set((state) => ({ count: state.count - 1 })),
253
+ reset: () => set({ count: 0 }),
254
+ }));
255
+
256
+ // Expose store hook for components to subscribe
257
+ get useStore() {
258
+ return this.store;
259
+ }
320
260
 
321
- // We'll initialize this soon enough.
322
- setStateValue!: (newValue: string) => void;
261
+ // Getter to access current state from within the service
262
+ private get storeState() {
263
+ return this.store.getState();
264
+ }
323
265
 
324
- /** Can be used to update the {@link currentValue} of the `inputbox`. */
325
- setValue(newValue: string): void {
326
- this.currentValue = newValue;
266
+ // Business logic methods
267
+ increment() {
268
+ this.storeState.increment();
269
+ }
327
270
 
328
- this.setStateValue(this.currentValue);
271
+ decrement() {
272
+ this.storeState.decrement();
329
273
  }
330
- }
331
274
 
332
- export const InputboxModuleBp = ProviderModule.blueprint({
333
- id: 'ComponentInputboxModule',
334
- provides: [InputboxService],
335
- exports: [InputboxService],
336
- });
337
- ```
275
+ reset() {
276
+ this.storeState.reset();
277
+ }
338
278
 
339
- `inputbox.tsx`
279
+ incrementBy(amount: number) {
280
+ // Complex logic lives in the service
281
+ const currentCount = this.storeState.count;
282
+ this.store.setState({ count: currentCount + amount });
283
+ }
340
284
 
341
- ```tsx
342
- export interface InputboxProps {
343
- initialValue: string;
285
+ async incrementAsync() {
286
+ // Handle async operations in the service
287
+ await new Promise((resolve) => setTimeout(resolve, 1000));
288
+ this.increment();
289
+ }
344
290
  }
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
291
  ```
358
292
 
359
- <hr>
360
-
361
- > Listview
362
-
363
- `listview.service.ts`
364
-
365
293
  ```ts
366
- @Injectable()
367
- export class ListviewService {
368
- items = [];
294
+ // counter.module.ts
369
295
 
370
- /* Remaining fancy implementation */
371
- }
296
+ import { ProviderModule } from '@adimm/x-injection-reactjs';
297
+
298
+ import { CounterService } from './counter.service';
372
299
 
373
- export const ListviewModuleBp = ProviderModule.blueprint({
374
- id: 'ComponentListviewModule',
375
- provides: [ListviewService],
376
- exports: [ListviewService],
300
+ export const CounterModuleBp = ProviderModule.blueprint({
301
+ id: 'CounterModule',
302
+ providers: [CounterService],
303
+ exports: [CounterService],
377
304
  });
378
305
  ```
379
306
 
380
- `listview.tsx`
381
-
382
307
  ```tsx
383
- export interface ListviewProps {
384
- items: any[];
385
- }
308
+ // counter.component.tsx
309
+
310
+ import { provideModuleToComponent, useInject } from '@adimm/x-injection-reactjs';
311
+
312
+ import { CounterModuleBp } from './counter.module';
313
+ import { CounterService } from './counter.service';
386
314
 
387
- export const Listview = provideModuleToComponent<ListviewProps>(ListviewModuleBp, ({ items }) => {
388
- const service = useInject(ListviewService);
315
+ const Counter = provideModuleToComponent(CounterModuleBp, () => {
316
+ // Inject service for business logic
317
+ const counterService = useInject(CounterService);
389
318
 
390
- /* Remaining fancy implementation */
319
+ // Subscribe to store for reactive state
320
+ const count = counterService.useStore((state) => state.count);
391
321
 
392
322
  return (
393
323
  <div>
394
- {service.items.map((item) => (
395
- <span key={item}>{item}</span>
396
- ))}
324
+ <h2>Count: {count}</h2>
325
+ <button onClick={() => counterService.increment()}>+1</button>
326
+ <button onClick={() => counterService.decrement()}>-1</button>
327
+ <button onClick={() => counterService.incrementBy(5)}>+5</button>
328
+ <button onClick={() => counterService.incrementAsync()}>+1 Async</button>
329
+ <button onClick={() => counterService.reset()}>Reset</button>
397
330
  </div>
398
331
  );
399
332
  });
333
+
334
+ export default Counter;
400
335
  ```
401
336
 
402
- <hr>
337
+ **Key Benefits:**
403
338
 
404
- > Dropdown
339
+ - **Encapsulation**: Store is encapsulated within the service, not exposed globally
340
+ - **Separation of concerns**: Business logic in services, UI only subscribes to state
341
+ - **Testability**: Services are self-contained and easy to test
342
+ - **Reusability**: Services with stores can be shared across components via dependency injection
343
+ - **Type safety**: Full TypeScript support throughout
405
344
 
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.
345
+ ### Parent-Child Provider Control
407
346
 
408
- `dropdown.service.ts`
347
+ Parent components can control child component dependencies using the `inject` prop:
409
348
 
410
349
  ```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
- ],
350
+ // Child module and service
351
+ const ChildModuleBp = ProviderModule.blueprint({
352
+ id: 'ChildModule',
353
+ providers: [ChildService],
354
+ exports: [ChildService],
433
355
  });
434
- ```
435
-
436
- `dropdown.tsx`
437
-
438
- ```tsx
439
- export interface DropdownProps {
440
- listviewProps: ListviewProps;
441
-
442
- initialSelectedValue: number;
443
- }
444
356
 
445
- export const Dropdown = provideModuleToComponent<DropdownProps>(
446
- ListviewModuleBp,
447
- ({ listviewProps, initialSelectedValue }) => {
448
- const service = useInject(DropdownService);
449
-
450
- /* Remaining fancy implementation */
357
+ // Parent module
358
+ const ParentModuleBp = ProviderModule.blueprint({
359
+ id: 'ParentModule',
360
+ providers: [ParentService, ChildService],
361
+ exports: [ParentService, ChildService],
362
+ });
451
363
 
452
- return (
453
- <div className="fancy-dropdown">
454
- <span>{initialSelectedValue}</span>
364
+ // Parent component controls child's service
365
+ const ParentComponent = provideModuleToComponent(ParentModuleBp, () => {
366
+ const childService = useInject(ChildService);
455
367
 
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
- );
368
+ // Override child's ChildService with parent's instance
369
+ return <ChildComponent inject={[{ provide: ChildService, useValue: childService }]} />;
370
+ });
463
371
  ```
464
372
 
465
- <hr>
466
-
467
- > Autocomplete
373
+ This pattern is useful for:
468
374
 
469
- And finally the grand finale!
375
+ - Building composable component hierarchies
376
+ - Sharing state between parent and child components
377
+ - Creating flexible component APIs
470
378
 
471
- `autocomplete.service.ts`
379
+ ## Advanced Usage
472
380
 
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
- }
381
+ ### Module Imports and Exports
491
382
 
492
- /* Remaining fancy implementation */
493
- }
383
+ Modules can import and re-export other modules:
494
384
 
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.
385
+ ```ts
386
+ const DropdownModuleBp = ProviderModule.blueprint({
387
+ id: 'DropdownModule',
388
+ imports: [ListviewModuleBp], // Import ListviewModule
389
+ providers: [DropdownService],
390
+ exports: [
391
+ ListviewModuleBp, // Re-export imported module
392
+ DropdownService,
393
+ ],
501
394
  });
502
395
  ```
503
396
 
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);
397
+ ### Multiple Dependency Injection
516
398
 
517
- service.inputboxService.currentValue = currentText;
399
+ Use `useInjectMany` to inject multiple dependencies:
518
400
 
519
- console.log(service.dropdownService.listviewService.items);
520
- // Produces: [29, 9, 1969]
521
-
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
- );
401
+ ```ts
402
+ const [userService, apiService] = useInjectMany([UserService, ApiService]);
533
403
  ```
534
404
 
535
- This should cover the fundamentals of how you can build a scalable UI by using the `xInjection` Dependency Injection 😊
536
-
537
- ## Unit Tests
405
+ ## Unit Testing
538
406
 
539
- It is very easy to create mock modules so you can provide them to your components in your unit tests.
407
+ Mock modules easily for testing:
540
408
 
541
409
  ```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
- }
549
-
550
- private async sendToLocation(user: User, location: any): Promise<any> {}
551
- }
410
+ import { act, render } from '@testing-library/react';
552
411
 
553
- const ApiModuleBp = new ProviderModule.blueprint({
412
+ // Original module
413
+ const ApiModuleBp = ProviderModule.blueprint({
554
414
  id: 'ApiModule',
555
415
  providers: [UserService, ApiService],
556
416
  });
557
417
 
558
- // Clone returns a `deep` clone and wraps all the `methods` to break their reference!
418
+ // Create mocked version
559
419
  const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
560
420
  id: 'ApiModuleMocked',
561
421
  providers: [
562
- {
563
- provide: UserService,
564
- useClass: UserService_Mock,
565
- },
422
+ { provide: UserService, useClass: UserServiceMock },
566
423
  {
567
424
  provide: ApiService,
568
425
  useValue: {
569
- sendRequest: async (location) => {
570
- console.log(location);
571
- },
426
+ sendRequest: vi.fn().mockResolvedValue({ data: 'mocked' }),
572
427
  },
573
428
  },
574
429
  ],
575
430
  });
576
431
 
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} />));
432
+ // Test with mocked module
433
+ await act(async () => render(<MyComponent module={ApiModuleBpMocked} />));
434
+ ```
435
+
436
+ **Testing with Zustand:**
437
+
438
+ ```tsx
439
+ import { act, renderHook } from '@testing-library/react';
440
+
441
+ import { CounterService } from './counter.service';
442
+
443
+ it('should increment counter via service', () => {
444
+ const service = new CounterService();
445
+
446
+ const { result } = renderHook(() => service.useStore((s) => s.count));
447
+
448
+ expect(result.current).toBe(0);
449
+
450
+ act(() => {
451
+ service.increment();
452
+ });
453
+
454
+ expect(result.current).toBe(1);
455
+ });
456
+
457
+ it('should handle complex business logic', () => {
458
+ const service = new CounterService();
459
+
460
+ act(() => {
461
+ service.incrementBy(10);
462
+ });
463
+
464
+ expect(service.useStore.getState().count).toBe(10);
465
+ });
579
466
  ```
580
467
 
581
468
  ## Documentation
582
469
 
583
- Comprehensive, auto-generated documentation is available at:
470
+ 📚 **Full API Documentation:** [https://adimarianmutu.github.io/x-injection-reactjs](https://adimarianmutu.github.io/x-injection-reactjs/index.html)
584
471
 
585
- 👉 [https://adimarianmutu.github.io/x-injection-reactjs/index.html](https://adimarianmutu.github.io/x-injection-reactjs/index.html)
472
+ For more information about the base library, see [xInjection Documentation](https://github.com/AdiMarianMutu/x-injection#readme).
586
473
 
587
474
  ## Contributing
588
475
 
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.
476
+ Pull requests are welcome! Please ensure your contributions follow the project's code style.
592
477
 
593
- ## Credits
478
+ ## License
594
479
 
595
- - [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/) - Author of `xInjection` & `xInjection ReactJS`
480
+ MIT © [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/)
596
481
 
597
482
  ---
598
483
 
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.**
484
+ **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.4",
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": {