typescriptreact-nativesingletoninversion-of-controlinversifyjs

Clear inversify-js container and resolve new service instances


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


Solution

  • 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.