cheap-di-react
Advanced tools
Comparing version 3.3.1 to 4.0.0
@@ -1,2 +0,2 @@ | ||
export * from './useContainer'; | ||
export * from './useDiContext'; | ||
export * from './use'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
tslib_1.__exportStar(require("./useContainer"), exports); | ||
tslib_1.__exportStar(require("./useDiContext"), exports); | ||
tslib_1.__exportStar(require("./use"), exports); | ||
//# sourceMappingURL=index.js.map |
export * from './DiContext'; | ||
export * from './decorators'; | ||
export * from './Providers'; | ||
export * from './hooks/use'; | ||
export * from './hooks'; |
@@ -5,5 +5,4 @@ "use strict"; | ||
tslib_1.__exportStar(require("./DiContext"), exports); | ||
tslib_1.__exportStar(require("./decorators"), exports); | ||
tslib_1.__exportStar(require("./Providers"), exports); | ||
tslib_1.__exportStar(require("./hooks/use"), exports); | ||
tslib_1.__exportStar(require("./hooks"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { DependencyRegistrator, ImplementationType } from 'cheap-di'; | ||
import { Container, DependencyRegistrator, ImplementationType } from 'cheap-di'; | ||
import { FC, ReactNode } from 'react'; | ||
@@ -8,2 +8,3 @@ declare type Dependency = (dependencyRegistrator: DependencyRegistrator) => void; | ||
self?: SelfDependency[]; | ||
parentContainer?: Container; | ||
/** if it is provided, logging will be enabled */ | ||
@@ -10,0 +11,0 @@ debugName?: string; |
@@ -7,14 +7,12 @@ "use strict"; | ||
const react_1 = require("react"); | ||
const decorators_1 = require("../decorators"); | ||
const DiContext_1 = require("../DiContext"); | ||
const hooks_1 = require("../hooks"); | ||
const configureStateful_1 = require("../hooks/configureStateful"); | ||
const utils_1 = require("../utils"); | ||
const DIProvider = props => { | ||
const { dependencies, self, debugName, children, } = props; | ||
const { dependencies, self, parentContainer, debugName, children, } = props; | ||
const [logger] = (0, react_1.useState)(() => new utils_1.InternalLogger(debugName)); | ||
const [initialized, setInitialized] = (0, react_1.useState)(false); | ||
const timerRef = (0, react_1.useRef)(null); | ||
const [contextValue, rerender] = (0, hooks_1.useContainer)(logger); | ||
const container = contextValue.container; | ||
const diContext = (0, hooks_1.useDiContext)({ logger, parentContainer }); | ||
const container = diContext.container; | ||
(0, react_1.useEffect)(() => { | ||
@@ -38,12 +36,5 @@ var _a; | ||
logger.log('singleton', constructor, 'founded'); | ||
const instance = container.resolve(type); | ||
(0, configureStateful_1.configureStateful)(instance, container.rootContainer.rerender, logger); | ||
// resolve type to get and register its instance | ||
container.resolve(type); | ||
} | ||
if ((0, decorators_1.isStateful)(constructor)) { | ||
logger.log('stateful', constructor, 'founded'); | ||
container.skipParentInstanceResolvingOnce(); // target instance should be instantiated from zero | ||
const instance = container.resolve(type); | ||
container.registerInstance(instance).as(type); | ||
(0, configureStateful_1.configureStateful)(instance, rerender, logger); | ||
} | ||
} | ||
@@ -69,3 +60,3 @@ if (container.parentContainer && singletonsSizeBeforeDependenciesUpdate !== container.getSingletons().size) { | ||
} | ||
return ((0, jsx_runtime_1.jsx)(DiContext_1.DiContext.Provider, Object.assign({ value: contextValue }, { children: (0, jsx_runtime_1.jsx)(MemoizedChildren, { children: children }) }))); | ||
return ((0, jsx_runtime_1.jsx)(DiContext_1.DiContext.Provider, Object.assign({ value: diContext }, { children: (0, jsx_runtime_1.jsx)(MemoizedChildren, { children: children }) }))); | ||
}; | ||
@@ -72,0 +63,0 @@ const MemoizedChildren = (0, react_1.memo)(({ children }) => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: children })); |
@@ -8,3 +8,2 @@ "use strict"; | ||
const cheap_di_1 = require("cheap-di"); | ||
const decorators_1 = require("../decorators"); | ||
const DIProvider_1 = require("./DIProvider"); | ||
@@ -142,52 +141,2 @@ const DIOneTimeProvider_1 = require("./DIOneTimeProvider"); | ||
})); | ||
test('stateful', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { | ||
let MyStateful = class MyStateful { | ||
constructor() { | ||
this.message = 'initial'; | ||
} | ||
}; | ||
MyStateful = tslib_1.__decorate([ | ||
decorators_1.stateful | ||
], MyStateful); | ||
const dependencies = [ | ||
dr => dr.registerType(MyStateful), | ||
]; | ||
const firstMessage = 'level 1'; | ||
const secondMessage = 'level 2'; | ||
const button1 = 'my-button-1'; | ||
const button2 = 'my-button-2'; | ||
const RootComponent = () => { | ||
return ((0, jsx_runtime_1.jsxs)(DIProvider_1.DIProvider, Object.assign({ dependencies: dependencies }, { children: [(0, jsx_runtime_1.jsx)(LoadComponent, { message: firstMessage, buttonId: button1 }), (0, jsx_runtime_1.jsx)(ReadComponent, {}), (0, jsx_runtime_1.jsxs)(DIProvider_1.DIProvider, Object.assign({ dependencies: dependencies }, { children: [(0, jsx_runtime_1.jsx)(LoadComponent, { message: secondMessage, buttonId: button2 }), (0, jsx_runtime_1.jsx)(ReadComponent, {})] }))] }))); | ||
}; | ||
const LoadComponent = ({ message, buttonId }) => { | ||
const myStateful = (0, hooks_1.use)(MyStateful); | ||
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("button", { "data-testid": buttonId, onClick: () => { | ||
myStateful.message = message; | ||
} }) })); | ||
}; | ||
const ReadComponent = () => { | ||
const myStateful = (0, hooks_1.use)(MyStateful); | ||
return ((0, jsx_runtime_1.jsx)("span", { children: myStateful.message })); | ||
}; | ||
const { queryAllByText, getByTestId, rerender } = (0, react_1.render)((0, jsx_runtime_1.jsx)(RootComponent, {})); | ||
expect(queryAllByText('initial').length).toBe(2); | ||
expect(queryAllByText(firstMessage).length).toBe(0); | ||
expect(queryAllByText(secondMessage).length).toBe(0); | ||
// first state | ||
yield (0, react_1.act)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { | ||
react_1.fireEvent.click(getByTestId(button1)); | ||
})); | ||
rerender((0, jsx_runtime_1.jsx)(RootComponent, {})); | ||
expect(queryAllByText('initial').length).toBe(1); | ||
expect(queryAllByText(firstMessage).length).toBe(1); | ||
expect(queryAllByText(secondMessage).length).toBe(0); | ||
// second state | ||
yield (0, react_1.act)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { | ||
react_1.fireEvent.click(getByTestId(button2)); | ||
})); | ||
rerender((0, jsx_runtime_1.jsx)(RootComponent, {})); | ||
expect(queryAllByText('initial').length).toBe(0); | ||
expect(queryAllByText(firstMessage).length).toBe(1); | ||
expect(queryAllByText(secondMessage).length).toBe(1); | ||
})); | ||
test('nested singletons', () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { | ||
@@ -194,0 +143,0 @@ let Service1 = class Service1 { |
@@ -1,2 +0,2 @@ | ||
import { AbstractConstructor, Constructor, ContainerImpl, ImplementationType, ImplementationTypeWithInjection, RegistrationType } from 'cheap-di'; | ||
import { AbstractConstructor, Constructor, ContainerImpl, ImplementationTypeWithInjection } from 'cheap-di'; | ||
export declare class ReactContainer extends ContainerImpl { | ||
@@ -8,14 +8,5 @@ parentContainer?: ReactContainer | undefined; | ||
constructor(parentContainer?: ReactContainer | undefined); | ||
registerType<TInstance>(implementationType: ImplementationType<TInstance>): { | ||
as: <TBase extends Partial<TInstance>>(type: RegistrationType<TBase>) => { | ||
with: (...injectionParams: any[]) => void; | ||
}; | ||
asSingleton: <TBase_1 extends Partial<TInstance>>(type?: RegistrationType<TBase_1> | undefined) => { | ||
with: (...injectionParams: any[]) => void; | ||
}; | ||
with: (...injectionParams: any[]) => void; | ||
}; | ||
get rootContainer(): ReactContainer; | ||
sameParent(parentContainer?: ContainerImpl): boolean; | ||
getDependencies(): Map<RegistrationType<any>, Object | ImplementationTypeWithInjection<any>>; | ||
getDependencies(): Map<import("cheap-di/dist/types").RegistrationType<any>, Object | ImplementationTypeWithInjection<any>>; | ||
localScope<Callback extends (container: ReactContainer) => any>(callback: Callback): Callback extends (container: ReactContainer) => infer Result ? Result : void; | ||
@@ -22,0 +13,0 @@ skipParentInstanceResolvingOnce(): void; |
@@ -5,4 +5,2 @@ "use strict"; | ||
const cheap_di_1 = require("cheap-di"); | ||
const decorators_1 = require("./decorators"); | ||
const stateful_1 = require("./decorators/stateful"); | ||
class ReactContainer extends cheap_di_1.ContainerImpl { | ||
@@ -16,14 +14,2 @@ constructor(parentContainer) { | ||
} | ||
registerType(implementationType) { | ||
if (!(0, decorators_1.isStateful)(implementationType)) { | ||
(0, decorators_1.stateful)(implementationType); | ||
} | ||
const registration = super.registerType(implementationType); | ||
const superAsSingleton = registration.asSingleton; | ||
registration.asSingleton = (type) => { | ||
(0, stateful_1.removeStateful)(implementationType); | ||
return superAsSingleton(type); | ||
}; | ||
return registration; | ||
} | ||
get rootContainer() { | ||
@@ -30,0 +16,0 @@ return this.findRootContainer(); |
export * from './InternalLogger'; | ||
export * from './reconfigureObject'; |
@@ -5,3 +5,2 @@ "use strict"; | ||
tslib_1.__exportStar(require("./InternalLogger"), exports); | ||
tslib_1.__exportStar(require("./reconfigureObject"), exports); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "cheap-di-react", | ||
"version": "3.3.1", | ||
"version": "4.0.0", | ||
"description": "", | ||
@@ -14,4 +14,7 @@ "scripts": { | ||
"@types/react": "18.0.12", | ||
"cheap-di": "3.4.0", | ||
"jest": "28.1.1", | ||
"jest-environment-jsdom": "28.1.1", | ||
"react": "17.0.2", | ||
"react-dom": "17.0.2", | ||
"ts-jest": "28.0.5", | ||
@@ -22,5 +25,5 @@ "tslib": "2.4.0", | ||
"peerDependencies": { | ||
"cheap-di": ">= 3.3.1", | ||
"react": ">= 16.14.0", | ||
"react-dom": ">= 16.14.0" | ||
"cheap-di": ">= 3.4.0", | ||
"react": ">= 17.0.2", | ||
"react-dom": ">= 17.0.2" | ||
}, | ||
@@ -27,0 +30,0 @@ "files": [ |
149
README.md
@@ -35,4 +35,4 @@ # cheap-di-react | ||
import { | ||
Provider, | ||
OneTimeProvider, | ||
DIProvider, | ||
DIOneTimeProvider, | ||
} from 'cheap-di-react'; | ||
@@ -44,3 +44,3 @@ import { Logger, ConsoleLogger } from './logger'; | ||
<> | ||
<Provider | ||
<DIProvider | ||
// will update dependencies on each render | ||
@@ -54,5 +54,5 @@ dependencies={[ | ||
<ComponentA/> | ||
</Provider> | ||
</DIProvider> | ||
<OneTimeProvider | ||
<DIOneTimeProvider | ||
// will use initial dependecies (it uses useMemo under hood) | ||
@@ -66,3 +66,3 @@ dependencies={[ | ||
<ComponentA/> | ||
</OneTimeProvider> | ||
</DIOneTimeProvider> | ||
</> | ||
@@ -87,141 +87,4 @@ ); | ||
If you mark your service as `@stateful` (or `@singleton`), Provider will create instance of the service and configures | ||
it fields (with Object.defineProperties), those fields changes (<b>reassign</b>) will trigger `Provider` rerender throw | ||
`React.Context`, and service consumers will receive field update. | ||
Difference between `@singleton` and `@stateful` that for `@singleton` there will be created only one instance for entire | ||
Provider tree, and for `@stateful` there will be created different instance per each Provider that register this type. | ||
`stateful` | ||
```tsx | ||
import { OneTimeProvider, use, stateful } from 'cheap-di-react'; | ||
@stateful | ||
class MyStateful { | ||
message: string = 'initial'; | ||
} | ||
const RootComponent = () => { | ||
return ( | ||
<OneTimeProvider self={[MyStateful]}> | ||
<LoadComponent message="level 1"/> | ||
<ReadComponent/> | ||
<OneTimeProvider self={[MyStateful]}> | ||
<LoadComponent message="level 2"/> | ||
<ReadComponent/> | ||
</OneTimeProvider> | ||
</OneTimeProvider> | ||
); | ||
}; | ||
const LoadComponent = ({ message }: { message: string }) => { | ||
const myStateful = use(MyStateful); | ||
return ( | ||
<button onClick={() => { myStateful.message = message; }}/> | ||
); | ||
}; | ||
const ReadComponent = () => { | ||
const myStateful = use(MyStateful); | ||
return ( | ||
<span> | ||
{myStateful.message} | ||
</span> | ||
); | ||
}; | ||
``` | ||
`singleton` | ||
```tsx | ||
import { singleton } from 'cheap-di'; | ||
import { OneTimeProvider, use } from 'cheap-di-react'; | ||
@singleton | ||
class MySingleton { | ||
data: string[] = ['initial']; | ||
async loadData() { | ||
this.data = await Promise.resolve(['some']); | ||
} | ||
} | ||
const RootComponent = () => { | ||
return ( | ||
<OneTimeProvider self={[MySingleton]}> | ||
<Component/> | ||
</OneTimeProvider> | ||
); | ||
}; | ||
const Component = () => { | ||
const mySingleton = use(MySingleton); | ||
useEffect(() => { | ||
(async () => { | ||
await mySingleton.loadData(); | ||
})(); | ||
}, []); | ||
return ( | ||
<div> | ||
{mySingleton.data.map(text => ( | ||
<span key={text} style={{ color: 'blue' }}> | ||
{text} | ||
</span> | ||
))} | ||
</div> | ||
); | ||
}; | ||
``` | ||
You can configure your services with more classic-way - when your class know nothing about how and where it will be | ||
used. | ||
`stateful` | ||
```tsx | ||
import { OneTimeProvider, use } from 'cheap-di-react'; | ||
class MyStateful { | ||
message: string = 'initial'; | ||
} | ||
const RootComponent = () => { | ||
return ( | ||
<OneTimeProvider dependencies={[ dr => dr.registerType(MyStateful) ]}> | ||
<LoadComponent message="level 1"/> | ||
<ReadComponent/> | ||
<OneTimeProvider dependencies={[ dr => dr.registerType(MyStateful) ]}> | ||
<LoadComponent message="level 2"/> | ||
<ReadComponent/> | ||
</OneTimeProvider> | ||
</OneTimeProvider> | ||
); | ||
}; | ||
``` | ||
`singleton` | ||
```tsx | ||
import { OneTimeProvider, use } from 'cheap-di-react'; | ||
class MySingleton { | ||
data: string[] = ['initial']; | ||
async loadData() { | ||
this.data = await Promise.resolve(['some']); | ||
} | ||
} | ||
const RootComponent = () => { | ||
return ( | ||
<OneTimeProvider dependencies={[ dr => dr.registerType(MySingleton).asSingleton() ]}> | ||
<Component/> | ||
</OneTimeProvider> | ||
); | ||
}; | ||
``` | ||
But remember, if you want to use auto resolving your dependencies with typescript reflection, you need | ||
`"emitDecoratorMetadata": true,` in `tsconfig.ts` and any class-decorator for your service-class (read more in | ||
`cheap-di` README.md) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75
130185
12
1069
86