1

I have a react-native application frontend using inversify-js.

I've structured some service classes to use IOC (using an inversify container), such services intended to be shared as just one singleton instances among other services. They have an init/destroy method to help to clear up the services' internal state.

The init/destroy mechanism works fine but on top of that, it would be nice to have a way to "clear" the inversify container singletons, to rebuild all my singleton services from scratch.

Example:

src/services/A.ts

@injectable()
export class A extends Service {
  constructor() {
    super();
  }

  init() {
    super.init();

    // [...] Initialize A's state
  }

  destroy() {
    // [...] Destroy A's state

    super.destroy();
  }

  method() {
    // [...] Provide functionality to other services (maintaining A's state)
  }
}

src/services/B.ts

@injectable()
export class B extends Service {
  constructor(
    b: B // receive injected instance of service A using constructor injection (inversify)
  ) {
    super();
  }

  init() {
    super.init();

    // [...] Initialize B's state
  }

  destroy() {
    // [...] Destroy B's state

    super.destroy();
  }

  method() {
    // [...] Provide functionality to other services (maintaining B's state, using also service A)
    this.a.method();
  }
}

src/inversify/container.ts

export const containerModule = new ContainerModule((bind) => {
  // Services
  bind<A>(A).toSelf().inSingletonScope();
  bind<B>(B).toSelf().inSingletonScope();
});

const container = new Container();

container.load(containerModule);

export default container;

index.ts

let a = container.get<A>(A);
let b = container.get<A>(B);

// [...] use services

// Destroy services (es: logout/reload app)
a.destroy();
b.destroy();

// Here I'd also like to "reset" the container
// container.reset(); // like this?

// [...] After some time (maybe login again?)

// I'd like these two to be new singleton instances (!== from previous a,b)
a = container.get<A>(A);
b = container.get<A>(B);

I should be able to maybe "unload" the ContainerModule from the Container (container.unload(containerModule)) and "load" it again? Seems hacky though, I was looking for comparable solutions.

I looked into this question but, in the end, didn't need the same "reset" functionality anyway: Reset scoped container in inversifyjs

Zorgatone
  • 4,176
  • 4
  • 30
  • 47
  • 1
    Hey there, I see this. But it’s really late in my timezone so allow me to get back to you like 12 hours later. – hackape Mar 26 '21 at 16:03
  • Thanks, take your time there's no rush – Zorgatone Mar 26 '21 at 16:06
  • hello @Zorgatone, I'm trying to use inversify for my react-native app, but I'm getting following errors: Require cycle: node_modules/inversify/lib/syntax/binding_on_syntax.js -> node_modules/inversify/lib/syntax/binding_when_syntax.js -> node_modules/inversify/lib/syntax/binding_on_syntax.js Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle. ERROR TypeError: Reflect.hasOwnMetadata is not a function. (In 'Reflect.hasOwnMetadata(metadataKey, annotationTarget)', 'Reflect.hasOwnMetadata' is undefined). Could you help me? – Prasanth Raj May 19 '21 at 18:36

2 Answers2

3

Use @preDestroy decorator for a class method. Inversify-docs#preDestroy

This decorator will run before a service is unbinded for any cached instance. For this reason, only bindings in singleton scope can contain a method with this decorator.

@preDestroy
destroy() {
  console.log('Service is about to be unbounded. Destroy');
}

am0wa
  • 7,724
  • 3
  • 38
  • 33
1

The solution I suggest in the other answer is to simply ditch the current container and create a fresh instance, very clean and easy to understand.

But then I look into the source code and find that such feature is already included.

container.unbindAll() it is!

This API unconditionally resets the container to a fresh state (almost fresh, explain later) without the need to create a new instance. Link to source code.

I said "almost fresh" earlier, this is because container also has a less known (at least to me) feature called snapshots. Turns out you can even have multiple snapshots of binding setups stored in one container.

Interesting . So if you make a snapshot of a empty container before registering any binding, and restore it later, it’s effective a "reset".

Lastly, container.unload(containerModule) is totally valid, not hacky at all. May call this one selective reset

If you read the source code you’ll see, under the hood of all these methods, it’s all about modifying the internal _bindingDictionary. That’s where all the bindings are stored.

hackape
  • 18,643
  • 2
  • 29
  • 57