Skip to main content

useFactory

Using useFactory in Providers

The useFactory option is the most powerful and flexible way to define dependencies in Depinjionary. It allows you to provide a factory function that creates and resolves the dependency, with full control over the instantiation process.

Features of useFactory

  1. Custom Instantiation Logic: You can define exactly how a dependency is created.
  2. Token Injection: The injectTokens option specifies the dependencies to be passed into the factory function.
  3. Support for Asynchronous Factories: Factory functions can be async, allowing you to perform asynchronous operations (e.g., fetching data, initializing context) before resolving the dependency.

Example: Custom Dependency Creation

Here’s a simple example of how to use useFactory to create a CalculatorService dependency:

const providers = [
{
provide: CalculatorService,
injectTokens: [LoggerInterface, StorageInterface, 'CalculatorConfig'],
useFactory: (logger: LoggerInterface, storage: StorageInterface, config: CalculatorConfig) => {
return new CalculatorService(logger, storage, config);
}
}
];

const container = ContainerResolver.init(providers);
const calculator = await container.resolve<CalculatorService>(CalculatorService);
console.log(calculator.calculate(5, 10)); // Outputs: 15

In this example:

  • LoggerInterface, StorageInterface, and 'CalculatorConfig' are the tokens injected into the factory.
  • The factory function creates a new instance of CalculatorService using these injected dependencies.

Example: Asynchronous Factory

With useFactory, you can also perform asynchronous tasks before resolving a dependency. For example:

const providers = [
{
provide: CalculatorService,
injectTokens: [LoggerInterface, StorageInterface, 'CalculatorConfig'],
useFactory: async (logger: LoggerInterface, storage: StorageInterface, config: CalculatorConfig) => {
const context: CalculatorContext = await CalculatorContextFactory(config).init();
return new CalculatorService(logger, storage, context);
}
}
];

const container = ContainerResolver.init(providers);
(async () => {
const calculator = await container.resolve<CalculatorService>(CalculatorService);
console.log(calculator.calculate(5, 10)); // Outputs: 15
})();

In this example:

  • The factory function asynchronously initializes a CalculatorContext before creating the CalculatorService.
  • This is particularly useful for dependencies requiring setup or fetching external data.

When to Use useFactory

  • Complex Dependencies: When a dependency requires initialization logic or complex construction.
  • Asynchronous Operations: When setup or configuration depends on async tasks (e.g., API calls, database initialization).
  • Dynamic Creation: When the exact dependency creation logic depends on runtime conditions.

Key Notes

  • Inject Tokens: Use the injectTokens option to specify which dependencies should be passed to the factory function.
  • Async Support: The factory function can be asynchronous; just ensure your application properly handles Promise resolutions when resolving dependencies.
  • Maximum Flexibility: useFactory gives you complete control over dependency creation, making it the most versatile provider option.