It would be nice to have special flag for ContextProvider that will forbid cloning the container. Below I'm trying to description why this might be useful and why current behavior is bit confusing (IMO).
With current behavior provided container is cloned and there no way to stop ContainerProvider from doing it.
I agree that this is how most of the users want it - combined with inContainerScope this logic always provide fresh clone so components can be independent. However, this approach works great if you don't have any "extra" bindings that depend on props. When all bindings declared prior cloning, then it works fine. In case where you want to a) create clone yourself and b) manage some init state of container/bindings this behavior causes some (IMO) unexpected behavior. To illustrate that I created simple example.
import { Container, token } from 'brandi';
import { useState } from 'react';
import { useInjection, ContainerProvider } from 'brandi-react';
let count = 0;
class MyService {
id: string;
key: string | null = null;
constructor() {
this.id = `MyService@${++count}`;
console.log(`${this.id}: Service created`);
}
setStorageKey(value: string): void {
console.log(this.id, 'setStorageKey', value);
this.key = value;
}
}
const TOKENS = {
myService: token<MyService>('myService'),
};
const baseContainer = new Container();
// Note: MyService uses container scope
baseContainer.bind(TOKENS.myService).toInstance(MyService).inContainerScope();
function useWidgetContainer(props: { storageKey: string }) {
const [containerCopy] = useState(function init() {
const copy = baseContainer.clone();
// ...
// ... some bindings that depends on props
// ...
// ... service initialization
copy.get(TOKENS.myService).setStorageKey(props.storageKey);
return copy;
});
return containerCopy;
}
function SomeChild() {
const srv = useInjection(TOKENS.myService);
return (
<div>
{srv.id}: {srv.key === null ? 'null' : srv.key}
</div>
);
}
export function Widget(props: { storageKey: string }) {
const container = useWidgetContainer(props);
// Because provider implicitly clones container, SomeChild will recive new instance of
// MyService (MyService@2) without storage key.
return (
<ContainerProvider container={container}>
<SomeChild />
</ContainerProvider>
);
}
In this example we have manually clone container in useWidgetContainer hook and using props to set some init state. The code in useWidgetContainer configure MyService in init function. Unfortunately because of hidden clone, SomeChild component will receive new instance of MyService that was not configured properly.
So, I hope this example shows that proposed enhancement is worth the work.
It would be nice to have special flag for ContextProvider that will forbid cloning the container. Below I'm trying to description why this might be useful and why current behavior is bit confusing (IMO).
With current behavior provided container is cloned and there no way to stop
ContainerProviderfrom doing it.I agree that this is how most of the users want it - combined with
inContainerScopethis logic always provide fresh clone so components can be independent. However, this approach works great if you don't have any "extra" bindings that depend on props. When all bindings declared prior cloning, then it works fine. In case where you want to a) create clone yourself and b) manage some init state of container/bindings this behavior causes some (IMO) unexpected behavior. To illustrate that I created simple example.In this example we have manually clone container in
useWidgetContainerhook and using props to set some init state. The code inuseWidgetContainerconfigure MyService ininitfunction. Unfortunately because of hidden clone,SomeChildcomponent will receive new instance of MyService that was not configured properly.So, I hope this example shows that proposed enhancement is worth the work.