molszanski / iti

~1kB Dependency Injection Library for Typescript and React with a unique support of async flow
https://itijs.org
MIT License
129 stars 6 forks source link

Event Hooks for dependencies #39

Closed jacoobes closed 1 year ago

jacoobes commented 1 year ago

Class-based / object dependencies could implement event hooks.

For example

class Logger implements Init {
     init() {
       this.info("logger started");
     }
}

or

{ init : () => this.info("logger started") }

There could be various hooks on dependencies that would be called when an iti even occurs. Describe the solution you'd like A clear and concise description of what you want to happen. Naive solution For any / all events, loop through dependencies, calling corresponding function.

//init event occurs
for(dependency of Object.values(cache)) {
   dependency?.init?.()
}
molszanski commented 1 year ago

Not sure what you mean. There was a similar idea for disposing: https://github.com/molszanski/iti/issues/21

Do you want something like this?

import {createContainer, Init} from 'iti'
class Stuff implements Init {
 init(){}
} 

const container = createContainer()
  .add({
    stuff: () => {
       const s = new Stuff()
       s.init() // <- your idea is for this to happen automagically?
    }
  })
molszanski commented 1 year ago

Is this somehow related to this code? https://github.com/sern-handler/handler/blob/main/src/handler/dependencies/provider.ts

jacoobes commented 1 year ago

Yes that code snippet is related.

Not sure what you mean. There was a similar idea for disposing: #21

Do you want something like this?

import {createContainer, Init} from 'iti'
class Stuff implements Init {
 init(){}
} 

const container = createContainer()
  .add({
    stuff: () => {
       const s = new Stuff()
       s.init() // <- your idea is for this to happen automagically?
    }
  })

Yes, something similar to this. Was wondering if iti could automatically call init whenever a class / object has the init method. the "init event" would be when the container is created. I realize this might be specific to my own project, and may be out of the scope of iti

molszanski commented 1 year ago

This feels like this should be one of helper utils.

What do you think?

// This code works, but just needs improved types
// const container = createContainer().add() 
container.on("containerUpserted", async (event) => {
  const item = await event.newContainer
  // @ts-ignore
  if (typeof item.init === "function") {
    // @ts-ignore
    item.init()
  }
})

I should update the event section of the docs and examples: https://itijs.org/docs/api#events

https://stackblitz.com/edit/github-ocqyjw?file=src%2F_0.business-logic.ts,src%2F_1.wiring.ts,src%2FApp.tsx,src%2F_2.hooks.ts

CleanShot 2023-02-16 at 23 06 17@2x
jacoobes commented 1 year ago

Hey, this looks great! Yeah! I would recommended adding interfaces for better typings, something like this:

interface Init {
  init() : void //or unknown
}

and a type predicate also

function isInitable(a: unknown): a is Init {
   return typeof a === 'object' && typeof a['init'] === 'function'
}
 const item = await event.newContainer
 if (isInitable(item)) {
   //should work without ts-ignore now
    item.init()
 }

I would also recommend adding a method for disposing also, so just like Init, an interface Disposable could be implemented for dependencies. but addDisposer already works, and I feel like it would be redundant to suggest that. What do you think?

For some reason, the stack blitz example not working on my computer. I assume the dependencies should call their init functions

molszanski commented 1 year ago

Cool. Glad to help. I think this is a good idea to collect those tricks into an extras module and document them. And thanks for better types ;)

jacoobes commented 1 year ago

Hope all goes well!

jacoobes commented 1 year ago

This feels like this should be one of helper utils.

What do you think?

// This code works, but just needs improved types
// const container = createContainer().add() 
container.on("containerUpserted", async (event) => {
  const item = await event.newContainer
  // @ts-ignore
  if (typeof item.init === "function") {
    // @ts-ignore
    item.init()
  }
})

I should update the event section of the docs and examples: https://itijs.org/docs/api#events

https://stackblitz.com/edit/github-ocqyjw?file=src%2F_0.business-logic.ts,src%2F_1.wiring.ts,src%2FApp.tsx,src%2F_2.hooks.ts

CleanShot 2023-02-16 at 23 06 17@2x

Hey, I have a question. I'm working on the init hooks as discussed. It does work, but only on eager dependencies, or those that have been wired into another dependency. I think its because when the lazy instance is put into iti, it is upserted as a function instead of an object, and therefore the check fails. I wonder if there's a better way around creating eager dependencies. What do you think?

https://github.com/sern-handler/handler/blob/feat/multiplatform/src/core/structures/container.ts At the moment, its not complete, but you get the idea😅

molszanski commented 1 year ago

I think your approach is fine and should work without problems.

Almost all DI / container solutions have this lifecycle control of Transient / Singleton (stateful) approach So this init calling would fail transient deps. But should work on containerRequested type of event 🤔

jacoobes commented 1 year ago

Makes sense. Thank you! I'll keep you posted. Thanks for a great module 😼

molszanski commented 1 year ago

@jacoobes glad it helps ^_^