Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Commit

Permalink
Adjusted StateTracker binding & added useInject wrapper hook
Browse files Browse the repository at this point in the history
  • Loading branch information
luvies committed Feb 28, 2019
1 parent daf10d8 commit 510fc00
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 22 deletions.
24 changes: 10 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export class DataService extends ReactiveService<State> {
}
```

You can then create an Inversify container with this service bound to it, and define a module that provides the provider component and HOC decorator.
You can then create an Inversify container with this service bound to it, and define a module that provides the provider component, HOC decorator, and the hook.

```ts
// injection.ts
import { createInjection } from 'react-injection';

export { InjectionProvider, injectComponent } = createInjection();
export { InjectionProvider, injectComponent, useInject } = createInjection();
```

You can then consume the service from your components like so:
Expand Down Expand Up @@ -120,17 +120,17 @@ The `injectComponent` decorator supports containers being passed directly as the
import { StateTracker } from 'react-injection';

// Bind the class manually
container.bind(StateTracker).toSelf().inSingletonScope();
StateTracker.bindToContainer(container);
```

It *must* be bound to itself and in the singleton scope to work properly, otherwise state update might not propagate properly from services. If you do not bind this class like this, then non of the services that inherit `ReactiveService` will work at all (since they require the class to be injected via props). If you do not use `ReactiveService`, then you do not need to do this binding, since it wil just skip it.
You need to do this whenever you do not use the `InjectionProvider` component provided in `createInjection`.

## Hook
To use the hook, you can do something like the following:

```tsx
// Imports from this module used in the example.
import { useInjection, InjectableProps } from 'react-injection';
import { useInjection, InjectableProps, StateTracker } from 'react-injection';

// Configure the container from somewhere.
const container = configureContainer();
Expand All @@ -140,6 +140,10 @@ const container = configureContainer();
// mix both kinds.
const context = createContext(container);

// If you use the provider directly, instead of the one given in `createInjection`,
// then you need to remember to do the following.
StateTracker.bindToContainer(container);

// Consume the services in the component.
interface InjectedProps {
dataService: DataService;
Expand All @@ -164,15 +168,7 @@ function App() {
}
```

If you plan on using the same context in a lot of places, you can easily wrap the hook:

```ts
export function useInject<T>(inject: InjectableProps<T>) {
return useInjection<T>(context, inject);
}
```

This means that the App component would look like this:
You can also use the `useInject` function provided in `createInjection`. Doing so would mean the App component would look like this:

```tsx
function App() {
Expand Down
12 changes: 9 additions & 3 deletions src/create-injection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Container, interfaces as inversifyTypes } from 'inversify';
import React, { Component, ComponentType, createContext, ReactNode } from 'react';
import { StateTracker } from './state-tracker';
import { useInjection } from './use-injection';

// ------ react-redux type definitions ------

Expand Down Expand Up @@ -61,16 +62,21 @@ export function createInjection(defaultContainer?: Container) {
super(props);

// Make sure StateTracker has been bound to the current container.
if (!props.container.isBound(StateTracker)) {
props.container.bind(StateTracker).toSelf().inSingletonScope();
}
StateTracker.bindToContainer(props.container);
}

public render() {
return <Provider value={this.props.container}>{this.props.children}</Provider>;
}
},

/**
* A wrapped version of the `useInjection` hook that uses the current context.
*/
useInject<T>(inject: InjectableProps<T>) {
return useInjection(context, inject);
},

/**
* Returns a function that will create a component that will have the requested services
* injected as props.
Expand Down
8 changes: 7 additions & 1 deletion src/state-tracker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { injectable } from 'inversify';
import { Container, injectable } from 'inversify';

export interface IStatefulService<TState> {
state: TState;
Expand All @@ -18,6 +18,12 @@ export interface StateChange<TState> {

@injectable()
export class StateTracker {
public static bindToContainer(container: Container) {
if (!container.isBound(StateTracker)) {
container.bind(StateTracker).toSelf().inSingletonScope();
}
}

public handlers = new Set<HandlerFn>();

private changes: Array<StateChange<any>> = [];
Expand Down
11 changes: 8 additions & 3 deletions src/use-injection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ export function useInjection<T>(
useEffect(() => {
const doUpdate = () => setTrigger({});

const stateTracker = container.get(StateTracker);
stateTracker.handlers.add(doUpdate);
let stateTracker: StateTracker | undefined;
if (container.isBound(StateTracker)) {
stateTracker = container.get(StateTracker);
stateTracker.handlers.add(doUpdate);
}

return () => {
stateTracker.handlers.delete(doUpdate);
if (stateTracker) {
stateTracker.handlers.delete(doUpdate);
}
};
});

Expand Down
2 changes: 1 addition & 1 deletion testing/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class SecondaryService {

export function initContainer(synchronous = false) {
const container = new Container();
container.bind(StateTracker).toSelf().inSingletonScope();
StateTracker.bindToContainer(container);
container.bind(sampleIdent).to(SampleService).inSingletonScope();
container.bind(SecondaryService).toSelf();

Expand Down

0 comments on commit 510fc00

Please sign in to comment.