Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

reactive-di

Package Overview
Dependencies
Maintainers
1
Versions
134
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

reactive-di - npm Package Compare versions

Comparing version 5.0.0 to 5.0.1

src/createMobxDetached.js

5

CHANGELOG.md

@@ -5,2 +5,7 @@ # Change Log

<a name="5.0.1"></a>
## [5.0.1](https://github.com/zerkalica/reactive-di/compare/v5.0.0...v5.0.1) (2017-11-04)
<a name="5.0.0"></a>

@@ -7,0 +12,0 @@ # [5.0.0](https://github.com/zerkalica/reactive-di/compare/v4.0.19...v5.0.0) (2017-10-29)

141

dist/reactive-di.es.js

@@ -1,3 +0,7 @@

import { ATOM_FORCE_CACHE, ATOM_FORCE_NONE, detached } from 'lom_atom';
var ATOM_FORCE_NONE = 0;
var ATOM_FORCE_CACHE = 1;
var renderedKey = Symbol('lom_rendered');
var diKey = Symbol('rdi_di');
var DisposableSheet = function () {

@@ -82,3 +86,3 @@ function DisposableSheet(key, sheet, remover) {

var sm = theme.sheetManager;
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this.__lom_di.instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this[diKey].instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
}

@@ -151,5 +155,6 @@ };

var depId = 0;
var rdiId = Symbol('rdi_id');
var Alias = function Alias(dest) {
dest.__rdi_id = '' + ++depId;
dest[rdiId] = '' + ++depId;
this.dest = dest;

@@ -182,8 +187,8 @@ };

} else {
if (src.__rdi_id === undefined) {
src.__rdi_id = '' + ++depId;
if (src[rdiId] === undefined) {
src[rdiId] = '' + ++depId;
}
var dest = item[1];
map[src.__rdi_id] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
map[src[rdiId]] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
}

@@ -193,7 +198,7 @@ } else {

if (_src.__rdi_id === undefined) {
_src.__rdi_id = '' + ++depId;
if (_src[rdiId] === undefined) {
_src[rdiId] = '' + ++depId;
}
map[_src.__rdi_id] = item;
map[_src[rdiId]] = item;
}

@@ -213,6 +218,6 @@ }

Injector.prototype.value = function value(key) {
var id = key.__rdi_id;
var id = key[rdiId];
if (key.__rdi_id === undefined) {
id = key.__rdi_id = '' + ++depId;
if (key[rdiId] === undefined) {
id = key[rdiId] = '' + ++depId;
}

@@ -226,3 +231,3 @@

value.displayName = this.displayName + "." + depName;
value.__lom_di = this;
value[diKey] = this;
var state = this._state === undefined ? undefined : this._state[depName];

@@ -318,6 +323,6 @@

if (id === undefined) {
id = key.__rdi_id;
id = key[rdiId];
if (id === undefined) {
id = key.__rdi_id = '' + ++depId;
id = key[rdiId] = '' + ++depId;
}

@@ -465,3 +470,3 @@ }

function createCreateElement(atomize, createElement) {
return function lomCreateElement() {
function lomCreateElement() {
var el = arguments[0];

@@ -541,8 +546,10 @@ var attrs = arguments[1];

}
};
}
return lomCreateElement;
}
function createReactWrapper(BaseComponent, defaultFromError) {
function createReactWrapper(BaseComponent, defaultFromError, detached) {
var _class;
var rootInjector = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Injector();
var rootInjector = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Injector();
var AtomizedComponent = (_class = function (_BaseComponent) {

@@ -588,6 +595,2 @@ inheritsLoose(AtomizedComponent, _BaseComponent);

AtomizedComponent.prototype.componentWillUnmount = function componentWillUnmount() {
this['AtomizedComponent.r()'].destructor();
};
AtomizedComponent.prototype.destructor = function destructor() {
this._el = undefined;

@@ -604,2 +607,4 @@ this._keys = undefined;

}
this['r()'].destructor();
};

@@ -619,2 +624,3 @@

});
error[renderedKey] = true;
}

@@ -719,3 +725,90 @@

export { createReactWrapper, createCreateElement, Injector, cloneComponent, theme, props };
function createMobxDetached(Reaction) {
var LomReaction = function () {
function LomReaction(name, host, propName, reactions) {
var _this = this;
this._next = undefined;
this._force = undefined;
this._cache = undefined;
this._host = host;
this._propName = propName;
this._reactions = reactions;
var onInvalidate = function onInvalidate() {
return _this._onInvalidate();
};
this._track = function () {
return _this.__track();
};
this._reaction = new Reaction(name, onInvalidate);
}
LomReaction.prototype._onInvalidate = function _onInvalidate() {
this._cache = undefined;
this.value();
};
LomReaction.prototype.__track = function __track() {
this._cache = this._host[this._propName](this._next, this._force);
};
LomReaction.prototype.value = function value(next, force) {
if (this._cache === undefined || force) {
this._next = next;
this._force = force;
this._reaction.track(this._track);
this._next = undefined;
this._force = undefined;
}
return this._cache;
};
LomReaction.prototype.destructor = function destructor() {
this._reaction.dispose();
this._reactions.delete(this);
this._reaction = undefined;
this._host = undefined;
this._cache = undefined;
this._track = undefined;
};
return LomReaction;
}();
return function mobxDetached(proto, name, descr) {
var value = descr.value;
var reactions = new WeakMap();
proto[name + '()'] = function getAtom() {
return reactions.get(this);
};
proto[name + '$'] = value;
return {
enumerable: descr.enumerable,
configurable: descr.configurable,
value: function value(next, force) {
var reaction = reactions.get(this);
if (!reaction) {
var di = this[diKey];
reaction = new LomReaction(di ? di.displayName + "." + name : name, this, name + '$', reactions);
reactions.set(this, reaction);
}
return reaction.value(next, force);
}
};
};
}
export { createReactWrapper, createCreateElement, Injector, cloneComponent, theme, props, createMobxDetached, renderedKey };
//# sourceMappingURL=reactive-di.es.js.map

@@ -5,4 +5,8 @@ 'use strict';

var lom_atom = require('lom_atom');
var ATOM_FORCE_NONE = 0;
var ATOM_FORCE_CACHE = 1;
var renderedKey = Symbol('lom_rendered');
var diKey = Symbol('rdi_di');
var DisposableSheet = function () {

@@ -87,3 +91,3 @@ function DisposableSheet(key, sheet, remover) {

var sm = theme.sheetManager;
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this.__lom_di.instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this[diKey].instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
}

@@ -156,5 +160,6 @@ };

var depId = 0;
var rdiId = Symbol('rdi_id');
var Alias = function Alias(dest) {
dest.__rdi_id = '' + ++depId;
dest[rdiId] = '' + ++depId;
this.dest = dest;

@@ -187,8 +192,8 @@ };

} else {
if (src.__rdi_id === undefined) {
src.__rdi_id = '' + ++depId;
if (src[rdiId] === undefined) {
src[rdiId] = '' + ++depId;
}
var dest = item[1];
map[src.__rdi_id] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
map[src[rdiId]] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
}

@@ -198,7 +203,7 @@ } else {

if (_src.__rdi_id === undefined) {
_src.__rdi_id = '' + ++depId;
if (_src[rdiId] === undefined) {
_src[rdiId] = '' + ++depId;
}
map[_src.__rdi_id] = item;
map[_src[rdiId]] = item;
}

@@ -218,6 +223,6 @@ }

Injector.prototype.value = function value(key) {
var id = key.__rdi_id;
var id = key[rdiId];
if (key.__rdi_id === undefined) {
id = key.__rdi_id = '' + ++depId;
if (key[rdiId] === undefined) {
id = key[rdiId] = '' + ++depId;
}

@@ -231,3 +236,3 @@

value.displayName = this.displayName + "." + depName;
value.__lom_di = this;
value[diKey] = this;
var state = this._state === undefined ? undefined : this._state[depName];

@@ -323,6 +328,6 @@

if (id === undefined) {
id = key.__rdi_id;
id = key[rdiId];
if (id === undefined) {
id = key.__rdi_id = '' + ++depId;
id = key[rdiId] = '' + ++depId;
}

@@ -470,3 +475,3 @@ }

function createCreateElement(atomize, createElement) {
return function lomCreateElement() {
function lomCreateElement() {
var el = arguments[0];

@@ -546,8 +551,10 @@ var attrs = arguments[1];

}
};
}
return lomCreateElement;
}
function createReactWrapper(BaseComponent, defaultFromError) {
function createReactWrapper(BaseComponent, defaultFromError, detached) {
var _class;
var rootInjector = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Injector();
var rootInjector = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Injector();
var AtomizedComponent = (_class = function (_BaseComponent) {

@@ -593,6 +600,2 @@ inheritsLoose(AtomizedComponent, _BaseComponent);

AtomizedComponent.prototype.componentWillUnmount = function componentWillUnmount() {
this['AtomizedComponent.r()'].destructor();
};
AtomizedComponent.prototype.destructor = function destructor() {
this._el = undefined;

@@ -609,2 +612,4 @@ this._keys = undefined;

}
this['r()'].destructor();
};

@@ -624,2 +629,3 @@

});
error[renderedKey] = true;
}

@@ -640,3 +646,3 @@

AtomizedComponent.prototype.render = function render() {
return this._el === undefined ? this.r(undefined, this._propsChanged ? lom_atom.ATOM_FORCE_CACHE : lom_atom.ATOM_FORCE_NONE) : this._el;
return this._el === undefined ? this.r(undefined, this._propsChanged ? ATOM_FORCE_CACHE : ATOM_FORCE_NONE) : this._el;
};

@@ -651,3 +657,3 @@

return AtomizedComponent;
}(BaseComponent), (_applyDecoratedDescriptor(_class.prototype, "r", [lom_atom.detached], Object.getOwnPropertyDescriptor(_class.prototype, "r"), _class.prototype)), _class);
}(BaseComponent), (_applyDecoratedDescriptor(_class.prototype, "r", [detached], Object.getOwnPropertyDescriptor(_class.prototype, "r"), _class.prototype)), _class);
return function reactWrapper(render) {

@@ -726,2 +732,89 @@ var displayName = render.displayName || render.name;

function createMobxDetached(Reaction) {
var LomReaction = function () {
function LomReaction(name, host, propName, reactions) {
var _this = this;
this._next = undefined;
this._force = undefined;
this._cache = undefined;
this._host = host;
this._propName = propName;
this._reactions = reactions;
var onInvalidate = function onInvalidate() {
return _this._onInvalidate();
};
this._track = function () {
return _this.__track();
};
this._reaction = new Reaction(name, onInvalidate);
}
LomReaction.prototype._onInvalidate = function _onInvalidate() {
this._cache = undefined;
this.value();
};
LomReaction.prototype.__track = function __track() {
this._cache = this._host[this._propName](this._next, this._force);
};
LomReaction.prototype.value = function value(next, force) {
if (this._cache === undefined || force) {
this._next = next;
this._force = force;
this._reaction.track(this._track);
this._next = undefined;
this._force = undefined;
}
return this._cache;
};
LomReaction.prototype.destructor = function destructor() {
this._reaction.dispose();
this._reactions.delete(this);
this._reaction = undefined;
this._host = undefined;
this._cache = undefined;
this._track = undefined;
};
return LomReaction;
}();
return function mobxDetached(proto, name, descr) {
var value = descr.value;
var reactions = new WeakMap();
proto[name + '()'] = function getAtom() {
return reactions.get(this);
};
proto[name + '$'] = value;
return {
enumerable: descr.enumerable,
configurable: descr.configurable,
value: function value(next, force) {
var reaction = reactions.get(this);
if (!reaction) {
var di = this[diKey];
reaction = new LomReaction(di ? di.displayName + "." + name : name, this, name + '$', reactions);
reactions.set(this, reaction);
}
return reaction.value(next, force);
}
};
};
}
exports.createReactWrapper = createReactWrapper;

@@ -733,2 +826,4 @@ exports.createCreateElement = createCreateElement;

exports.props = props;
exports.createMobxDetached = createMobxDetached;
exports.renderedKey = renderedKey;
//# sourceMappingURL=reactive-di.js.map
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('lom_atom')) :
typeof define === 'function' && define.amd ? define(['exports', 'lom_atom'], factory) :
(factory((global['reactive-di'] = {}),global.lom_atom));
}(this, (function (exports,lom_atom) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global['reactive-di'] = {})));
}(this, (function (exports) { 'use strict';
var ATOM_FORCE_NONE = 0;
var ATOM_FORCE_CACHE = 1;
var renderedKey = Symbol('lom_rendered');
var diKey = Symbol('rdi_di');
var DisposableSheet = function () {

@@ -86,3 +92,3 @@ function DisposableSheet(key, sheet, remover) {

var sm = theme.sheetManager;
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this.__lom_di.instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
return sm === undefined ? fakeSheet : sm.sheet(isInstance ? themeId + "[" + this[diKey].instance + "]" : themeId, value || this[themeId](), !!this[atomId]);
}

@@ -155,5 +161,6 @@ };

var depId = 0;
var rdiId = Symbol('rdi_id');
var Alias = function Alias(dest) {
dest.__rdi_id = '' + ++depId;
dest[rdiId] = '' + ++depId;
this.dest = dest;

@@ -186,8 +193,8 @@ };

} else {
if (src.__rdi_id === undefined) {
src.__rdi_id = '' + ++depId;
if (src[rdiId] === undefined) {
src[rdiId] = '' + ++depId;
}
var dest = item[1];
map[src.__rdi_id] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
map[src[rdiId]] = typeof dest === 'function' && !(dest instanceof Alias) ? new Alias(dest) : dest;
}

@@ -197,7 +204,7 @@ } else {

if (_src.__rdi_id === undefined) {
_src.__rdi_id = '' + ++depId;
if (_src[rdiId] === undefined) {
_src[rdiId] = '' + ++depId;
}
map[_src.__rdi_id] = item;
map[_src[rdiId]] = item;
}

@@ -217,6 +224,6 @@ }

Injector.prototype.value = function value(key) {
var id = key.__rdi_id;
var id = key[rdiId];
if (key.__rdi_id === undefined) {
id = key.__rdi_id = '' + ++depId;
if (key[rdiId] === undefined) {
id = key[rdiId] = '' + ++depId;
}

@@ -230,3 +237,3 @@

value.displayName = this.displayName + "." + depName;
value.__lom_di = this;
value[diKey] = this;
var state = this._state === undefined ? undefined : this._state[depName];

@@ -322,6 +329,6 @@

if (id === undefined) {
id = key.__rdi_id;
id = key[rdiId];
if (id === undefined) {
id = key.__rdi_id = '' + ++depId;
id = key[rdiId] = '' + ++depId;
}

@@ -469,3 +476,3 @@ }

function createCreateElement(atomize, createElement) {
return function lomCreateElement() {
function lomCreateElement() {
var el = arguments[0];

@@ -545,8 +552,10 @@ var attrs = arguments[1];

}
};
}
return lomCreateElement;
}
function createReactWrapper(BaseComponent, defaultFromError) {
function createReactWrapper(BaseComponent, defaultFromError, detached) {
var _class;
var rootInjector = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Injector();
var rootInjector = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Injector();
var AtomizedComponent = (_class = function (_BaseComponent) {

@@ -592,6 +601,2 @@ inheritsLoose(AtomizedComponent, _BaseComponent);

AtomizedComponent.prototype.componentWillUnmount = function componentWillUnmount() {
this['AtomizedComponent.r()'].destructor();
};
AtomizedComponent.prototype.destructor = function destructor() {
this._el = undefined;

@@ -608,2 +613,4 @@ this._keys = undefined;

}
this['r()'].destructor();
};

@@ -623,2 +630,3 @@

});
error[renderedKey] = true;
}

@@ -639,3 +647,3 @@

AtomizedComponent.prototype.render = function render() {
return this._el === undefined ? this.r(undefined, this._propsChanged ? lom_atom.ATOM_FORCE_CACHE : lom_atom.ATOM_FORCE_NONE) : this._el;
return this._el === undefined ? this.r(undefined, this._propsChanged ? ATOM_FORCE_CACHE : ATOM_FORCE_NONE) : this._el;
};

@@ -650,3 +658,3 @@

return AtomizedComponent;
}(BaseComponent), (_applyDecoratedDescriptor(_class.prototype, "r", [lom_atom.detached], Object.getOwnPropertyDescriptor(_class.prototype, "r"), _class.prototype)), _class);
}(BaseComponent), (_applyDecoratedDescriptor(_class.prototype, "r", [detached], Object.getOwnPropertyDescriptor(_class.prototype, "r"), _class.prototype)), _class);
return function reactWrapper(render) {

@@ -725,2 +733,89 @@ var displayName = render.displayName || render.name;

function createMobxDetached(Reaction) {
var LomReaction = function () {
function LomReaction(name, host, propName, reactions) {
var _this = this;
this._next = undefined;
this._force = undefined;
this._cache = undefined;
this._host = host;
this._propName = propName;
this._reactions = reactions;
var onInvalidate = function onInvalidate() {
return _this._onInvalidate();
};
this._track = function () {
return _this.__track();
};
this._reaction = new Reaction(name, onInvalidate);
}
LomReaction.prototype._onInvalidate = function _onInvalidate() {
this._cache = undefined;
this.value();
};
LomReaction.prototype.__track = function __track() {
this._cache = this._host[this._propName](this._next, this._force);
};
LomReaction.prototype.value = function value(next, force) {
if (this._cache === undefined || force) {
this._next = next;
this._force = force;
this._reaction.track(this._track);
this._next = undefined;
this._force = undefined;
}
return this._cache;
};
LomReaction.prototype.destructor = function destructor() {
this._reaction.dispose();
this._reactions.delete(this);
this._reaction = undefined;
this._host = undefined;
this._cache = undefined;
this._track = undefined;
};
return LomReaction;
}();
return function mobxDetached(proto, name, descr) {
var value = descr.value;
var reactions = new WeakMap();
proto[name + '()'] = function getAtom() {
return reactions.get(this);
};
proto[name + '$'] = value;
return {
enumerable: descr.enumerable,
configurable: descr.configurable,
value: function value(next, force) {
var reaction = reactions.get(this);
if (!reaction) {
var di = this[diKey];
reaction = new LomReaction(di ? di.displayName + "." + name : name, this, name + '$', reactions);
reactions.set(this, reaction);
}
return reaction.value(next, force);
}
};
};
}
exports.createReactWrapper = createReactWrapper;

@@ -732,2 +827,4 @@ exports.createCreateElement = createCreateElement;

exports.props = props;
exports.createMobxDetached = createMobxDetached;
exports.renderedKey = renderedKey;

@@ -734,0 +831,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

14

package.json
{
"name": "reactive-di",
"version": "5.0.0",
"version": "5.0.1",
"description": "Reactive dependency injection",

@@ -64,4 +64,8 @@ "publishConfig": {

"chokidar-cli": "^1.2.0",
"flow-bin": "^0.57.3",
"flow-bin": "^0.58.0",
"lom_atom": "^2.0.6",
"mobx": "^3.3.1",
"mocha": "^4.0.1",
"preact": "^8.2.6",
"preact-render-to-string": "^3.7.0",
"rimraf": "^2.6.2",

@@ -73,7 +77,5 @@ "rollup": "^0.48.2",

"standard-version": "^4.2.0",
"uglify-es": "^3.1.5"
"uglify-es": "^3.1.6"
},
"dependencies": {
"lom_atom": "^2.0.5"
}
"dependencies": {}
}

@@ -5,4 +5,14 @@ # Reactive DI [![Build Status](https://secure.travis-ci.org/zerkalica/reactive-di.png)](http://travis-ci.org/zerkalica/reactive-di)

Dependency injection with reactivity unobtrusive state-management in [mobx](https://mobx.js.org/) manner, applied to react-like components, css-in-js. Compatible with flow, react, but free from framework lock-in (no React.createElement, Inferno.createVNode), etc. Size about 10kb reactive-di.min.js + 11kb lom_atom.min.js
Typesafe dependency injection container for react-like components.
* With this DI you can buit pure component based architecture.
* With this DI you can forget about [HOC](https://reactjs.org/docs/higher-order-components.html) and any decorators around component.
* Atomatic isolated error and loading status handling for each component. Error exception in component breaks only this component.
* ReactiveDI helps you to follow [open/closed principle](https://en.wikipedia.org/wiki/Open/closed_principle) via slots (like [vue slots](https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots)).
* [Hierarchical Dependency Injectors](https://angular.io/guide/hierarchical-dependency-injection).
* ReactiveDI easily integrates some state management libraries: [MobX](htttps://mobx.js.org), [lom_atom](https://github.com/zerkalica/lom_atom).
* Framework agnostic, vendor lock-in free: no static dependencies from React, MobX, etc.
* Easily integrates css-in-js solutions like [jss](https://github.com/cssinjs/jss).
* Tiny size about 10kb reactive-di.min.js.
[example source](https://github.com/zerkalica/rdi-examples), [demo](http://zerkalica.github.io/rdi-examples/), [todomvc benchmark](http://mol.js.org/app/bench/#bench=https%3A%2F%2Fzerkalica.github.io%2Ftodomvc%2Fbenchmark%2F/sample=preact-lom-rdi~preact-raw~preact-mobx)

@@ -15,12 +25,15 @@

- [Hello world](#hello-world)
- [Setup with preact and lom_atom](#setup-with-preact-and-lomatom)
- [Setup with react and mobx](#setup-with-react-and-mobx)
- [Features](#features)
- [Typesafe context in components](#typesafe-context-in-components)
- [State management based on lom_atom](#state-management-based-on-lomatom)
- [Asynchronous code](#asynchronous-code)
- [Error handling](#error-handling)
- [Loading status handing](#loading-status-handing)
- [Registering default dependencies](#registering-default-dependencies)
- [Components cloning](#components-cloning)
- [State management based on lom_atom or mobx](#state-management-based-on-lomatom-or-mobx)
- [Automatic error handling](#automatic-error-handling)
- [Custom error handler](#custom-error-handler)
- [Loading status handling](#loading-status-handling)
- [Redefine default dependencies](#redefine-default-dependencies)
- [Components cloning and slots](#components-cloning-and-slots)
- [Hierarchical dependency injection](#hierarchical-dependency-injection)
- [Optional css-in-js support](#optional-css-in-js-support)
- [Multiple css instances](#multiple-css-instances)
- [Passing component props to its depenendencies](#passing-component-props-to-its-depenendencies)

@@ -57,3 +70,3 @@ - [React compatible](#react-compatible)

babel-plugin-transform-metadata is optional, used for metadata generation.
babel-plugin-transform-metadata is optional, used for metadata generation. ReactiveDI use type annotations for dependency resolving, without this plugin you will need to provide metadata manually.

@@ -70,8 +83,10 @@ ## Debug

Setup:
ReactiveDI has no static dependencies and not a zero-setup library. Setup is usually about 30-50 SLOC, but you do it once per application. But you can integrate into ReactiveDI any component react-like, state management and css-in-js library via adapters.
### Setup with preact and lom_atom
```js
// @flow
import {mem, action} from 'lom_atom'
import {createReactWrapper, createCreateElement, Injector} from 'reactive-di'
import {detached, mem, action} from 'lom_atom'
import {createReactWrapper, createCreateElement} from 'reactive-di'
import {render, h, Component} from 'preact'

@@ -99,5 +114,6 @@

Component,
ErrorableView
ErrorableView,
detached
),
h
(h: React$CreateElement)
)

@@ -110,138 +126,147 @@ global['lom_h'] = lomCreateElement

```js
import {mem} from 'lom_atom'
import {props} from 'reactive-di'
import {render} from 'preact'
interface IHelloProps {
name: string;
}
class HelloContext {
@mem name = ''
@mem name: string
@props set props({name}: IHelloProps) {
this.name = name
}
}
function HelloView(
{prefix}: {prefix: string},
{context}: {context: HelloContext}
export function HelloView(
_: IHelloProps,
{context}: {context: HelloContext}
) {
return <div>
{prefix}, {context.name}
<br/><input value={context.name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
Hello, {context.name}
<br/><input value={context.name} onInput={({target}) => {
context.name = (target: any).value
}} />
</div>
}
render(<HelloView prefix="Hello" />, document.body)
render(<HelloView name="John" />, document.body)
```
## Features
### Setup with react and mobx
### Typesafe context in components
```js
// @flow
function HelloView(
{prefix}: {prefix: string}, // props
{context}: {context: HelloContext} // automatically injected reactive context
) {
return <div>...</div>
import {Reaction} from 'mobx'
import {createReactWrapper, createCreateElement, creteMobxDetached} from 'reactive-di'
import {createElement, Component} from 'react'
import {render} from 'react-dom'
function ErrorableView({error}: {error: Error}) {
return <div>
{error instanceof mem.Wait
? <div>
Loading...
</div>
: <div>
<h3>Fatal error !</h3>
<div>{error.message}</div>
<pre>
{error.stack.toString()}
</pre>
</div>
}
</div>
}
const lomCreateElement = createCreateElement(
createReactWrapper(
Component,
ErrorableView,
creteMobxDetached(Reaction)
),
createElement
)
global['lom_h'] = lomCreateElement
```
Or
Usage:
```js
function HelloView(
_: {},
context: HelloComponent
import {observable} from 'mobx'
import {props} from 'reactive-di'
import {render} from 'preact'
interface IHelloProps {
name: string;
}
class HelloContext {
@observable name: string
@props set props({name}: IHelloProps) {
this.name = name
}
}
export function HelloView(
_: IHelloProps,
{context}: {context: HelloContext}
) {
/// ...
return <div>
Hello, {context.name}
<br/><input value={context.name} onInput={({target}) => {
context.name = (target: any).value
}} />
</div>
}
render(<HelloView name="John" />, document.body)
```
Context signature generated from component via [babel-plugin-transform-metadata](https://github.com/zerkalica/babel-plugin-transform-metadata).
## Features
Classes as keys. Without plugin, we need to define metadata manually:
### Typesafe context in components
```js
HelloView.deps = [{context: HelloContext}]
```
You can use [context in stateless functional components](https://reactjs.org/docs/context.html#referencing-context-in-stateless-functional-components). With [babel-plugin-transform-metadata](https://github.com/zerkalica/babel-plugin-transform-metadata) you do not need to provide metadata (like ``` Button.contextTypes = {color: PropTypes.string}; ```).
Injector in createElement (lom_h) automatically initializes HelloContext and pass it to HelloView in
Context signature generated from second argument:
```js
render(<HelloView prefix="Hello" />, document.body)
// @flow
export function HelloView(
_: IHelloProps,
{context}: {context: HelloContext}
) { ... }
```
### State management based on lom_atom
Or
Rdi based on [lom_atom](https://github.com/zerkalica/lom_atom), state management library, like mobx, but much simpler and with some killer features. Statefull or stateless components in rdi - pure functions.
Modifying state:
```js
import {action, mem} from 'lom_atom'
class HelloContext {
@mem name = ''
}
function HelloView(
_: {},
{context}: {context: HelloContext}
context: HelloComponent
) {
return <input value={context.name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
/// ...
}
```
All state changes are asynchronous, but for prevent loosing cursor position in react input, action helper used.
Class definitions used as keys for dependency resolving. For generation dependency metadata ReactiveDI do not use any library (like props-types), raw metadata only expose function arguments to injector. Without plugin, you will need to provide them manually:
### Asynchronous code
```js
HelloView.deps = [{context: HelloContext}]
```
Loading actual state:
Injector in createElement (lom_h) automatically initializes HelloContext and pass it to HelloView in
```js
import {mem, force} from 'lom_atom'
class HelloContext {
@force force: HelloContext
@mem set name(next: string | Error) {}
@mem get name(): string {
fetch('/hello')
.then((r: Response) => r.json())
.then((data: Object) => {
this.name = data.name
})
.catch((e: Error) => {
this.name = e
})
throw new mem.Wait()
}
}
function HelloView(
_: {},
context: HelloContext
) {
return <div>
<input value={context.name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
<button onClick={() => { context.forced.name }}>Reload from server</button>
</div>
}
render(<HelloView prefix="Hello" />, document.body)
```
First time ``` context.name ``` invokes fetch('/hello') and actualizes state, second time - ``` context.name ``` returns value from cache.
### State management based on lom_atom or mobx
``` context.forced.name ``` invokes fetch handler again.
ReactiveDI is state management agnostic library. You can use mobx or [lom_atom](https://github.com/zerkalica/lom_atom) (like mobx, but much simpler and with some killer features). In ReactiveDI all components are pure functional.
``` context.name = value ``` value sets directly into cache.
### Automatic error handling
``` context.forced.name = value ``` invokes set name handler in HelloContext and sets into cache.
All errors are isolated in components. They do not breaks whole application. You don't need to manually catch errors via [componentDidCatch](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html).
### Error handling
```js

@@ -258,11 +283,9 @@ class HelloContext {

) {
return <input value={context.name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
return <input value={context.name} onInput={({target}: Event) => {
context.name = (target: any).value
}} />
}
```
Accessing ``` context.name ``` throws oops, try/catch in HelloView wrapper displays default ErrorableView, registered in rdi:
Exception in ``` get name ``` intercepted by try/catch in HelloView wrapper and displays by default ErrorableView, registered in ReactiveDI setup:

@@ -291,3 +314,4 @@ ```js

Component,
ErrorableView
ErrorableView,
detached
),

@@ -299,4 +323,6 @@ h

We can manually handle error:
### Custom error handler
You can provide custom error component handler:
```js

@@ -314,36 +340,35 @@ function HelloView(

return <input value={name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
return <input value={name} onInput={{target}: Event) => {
context.name = (target: any).value
}} />
}
HelloView.onError = ({error: Error}) => (
<div>{error.message}</div>
)
```
### Loading status handing
Or manually handle error:
Looks like error handling. ``` throw new mem.Wait() ``` throws some specific exception.
```js
class HelloContext {
@force force: HelloContext
function HelloView(
_: {},
{context}: {context: HelloContext}
) {
let name: string
try {
name = context.name
} catch (e) {
name = 'Error:' + e.message
}
@mem set name(next: string | Error) {}
@mem get name(): string {
fetch('/hello')
.then((r: Response) => r.json())
.then((data: Object) => {
this.name = data.name
})
.catch((e: Error) => {
this.name = e
})
throw new mem.Wait()
}
return <input value={name} onInput={{target}: Event) => {
context.name = (target: any).value
}} />
}
```
Catched in HelloComponent wrapper and default ErrorableView shows loader instead of HelloView.
### Loading status handling
In ReactiveDI pending/complete status realized via exceptions. Special user defined "Wait" exception can be handled in ErrorableView.
```js

@@ -356,9 +381,3 @@ function ErrorableView({error}: {error: Error}) {

</div>
: <div>
<h3>Fatal error !</h3>
<div>{error.message}</div>
<pre>
{error.stack.toString()}
</pre>
</div>
: ...
}

@@ -369,27 +388,22 @@ </div>

We can manually define loader in component, using try/catch:
In component model ``` throw new mem.Wait() ``` catched in HelloComponent wrapper and default ErrorableView shows ``` Loading... ``` instead of HelloView.
```js
function HelloView(
_: {},
{context}: {context: HelloContext}
) {
let name: string
try {
name = context.name
} catch (e) {
if (e instanceof mem.Wait) { name = 'Loading...' }
else { throw e }
class HelloContext {
@force force: HelloContext
@mem set name(next: string | Error) {}
@mem get name(): string {
// fetch some data and update name
throw new mem.Wait()
}
return <input value={name} onInput={
action((e: Event) => {
context.name = (e.target: any).value
})
} />
}
```
### Registering default dependencies
On fetch complete ``` fetch().then((data: string) => {this.name = data}) ``` sets new data and render HelloView instead of ErrorableView.
### Redefine default dependencies
Class SomeAbstract used somewhere in the application, but at ReactiveDI setup you can redefine them to SomeConcrete class instance with same interface.
```js

@@ -414,9 +428,10 @@ class SomeAbstract {}

injector.value(SomeAbstract).a instanceof SomeConcrete
```
### Components cloning
### Components cloning and slots
Creates slightly modified component.
In vue you can use [content distribution with slots](https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots). ReactiveDI helps you to do same thing in react-applications. Slot is a component itself or its id.
Create slightly modified component, based on FirstCounterView.
```js

@@ -454,4 +469,10 @@ import {cloneComponent} from 'reactive-di'

Works like inheritance in classes, but you don't need to extract each component detail in methods. Any component part is open for extension bу default. Be careful, do not violate [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle).
### Hierarchical dependency injection
Each component instance has an own injector. Injector - is a cache map with instances, which types described in component context. Looks like angular [hierarchical dependency injection](https://angular.io/guide/hierarchical-dependency-injection), but no so complex.
When Parent and Child components depends on on same SharedService - DI injects one instance to them. And this instance live while Parent component mounted to DOM.
```js

@@ -467,2 +488,3 @@ class SharedService {}

function Child(
_: {},
context: {sharedService: SharedService}

@@ -474,2 +496,4 @@ ) {

If only Child component depends on SharedService, DI creates separated SharedService instance per Child.
```js

@@ -491,3 +515,3 @@ class SharedService {}

Via adapters rdi supports css-in-js with reactivity and dependency injection power:
Css-in-js with reactivity and dependency injection power. ReactiveDI not statically depended on [Jss](https://github.com/cssinjs/jss), you can integrate another css-in-js solution, realizing described below interface.

@@ -498,3 +522,3 @@ Setup:

// @flow
import {mem} from 'lom_atom'
import {detached, mem} from 'lom_atom'
import {createReactWrapper, createCreateElement, Injector} from 'reactive-di'

@@ -516,11 +540,6 @@

export interface ISheet<V: Object> {
update(name?: string, props: V): ISheet<V>;
attach(): ISheet<V>;
detach(): ISheet<V>;
classes: {+[id: $Keys<V>]: string};
}
*/
const defaultDeps = []
const injector = new Injector(defaultDeps, jss)
const lomCreateElement = createCreateElement(

@@ -530,3 +549,4 @@ createReactWrapper(

ErrorableView,
injector
detached,
new Injector([], jss)
),

@@ -538,7 +558,7 @@ h

Usage:
Reactive style usage:
```js
import {mem} from 'lom_atom'
import type {NamesOf} from 'lom_atom'
import {theme} from 'reactive-di'

@@ -574,7 +594,66 @@ class ThemeVars {

Styles automatically mounts/unmounts together with component. Changing ``` vars.color ``` automatically rebuilds and remounts css.
Whith lom_atom, styles automatically mounts/unmounts together with component. Changing ``` vars.color ``` automatically rebuilds and remounts css.
With mobx, unmount feature does not works at current moment. But still no memory leaks, due to unique theme id.
Without any state management library works only css mounting without reactivity updates.
```js
import {theme} from 'reactive-di'
class MyTheme {
@theme get css() {
return {
wrapper: {
backgroundColor: 'red'
}
}
}
}
function MyView(
props: {},
{theme: {css}, vars}: {theme: MyTheme}
) {
return <div class={css.wrapper}>...</div>
}
```
### Multiple css instances
By default one css block generated per component function. But you can generate unique css block per component instance too. Just use ``` theme.self ``` decorator:
```js
import {mem} from 'lom_atom'
import {props, theme} from 'reactive-di'
interface MyProps {
color: string;
}
class MyTheme {
@mem @props _props: MyProps
@mem @theme.self get css() {
return {
wrapper: {
backgroundColor: this.props.color
}
}
}
}
function MyView(
props: MyProps,
{theme: {css}}: {theme: MyTheme}
) {
return <div class={css.wrapper}>...
</div>
}
<MyView color="red"/>
<MyView color="blue"/>
```
### Passing component props to its depenendencies
Sometimes we need to pass component properties to its services.
You can pass component properties to its dependencies via ``` prop ``` decorator.

@@ -605,53 +684,36 @@ ```js

If you need to react on props changes - just use combination ``` @mem ``` and ``` @props ``` decorators.
```js
class MyViewService {
@mem @props _props: MyProps;
// @mem @props _props: MyProps; // for reactive props
@mem get some(): string {
return this._props.some + '-suffix'
}
}
```
### React compatible
We still can use any react/preact/inferno components together with rdi components.
You can use any react/preact/inferno components together with rdi components.
### Logging
Not ReactiveDi part, in state management libraries you can monitor state changes and user actions.
Console logger in lom_atom:
```js
import {defaultContext, BaseLogger} from 'lom_atom'
import {defaultContext, BaseLogger, ConsoleLogger} from 'lom_atom'
import type {ILogger} from 'lom_atom'
class Logger extends BaseLogger {
/**
* Invokes before atom creating
*
* @param host Object Object with atom
* @param field string property name
* @param key mixed | void for dictionary atoms - dictionary key
*/
create<V>(host: Object, field: string, key?: mixed): V | void {}
defaultContext.setLogger(new ConsoleLogger())
```
/**
* After atom destroy
*/
destroy(atom: IAtom<*>): void {}
For custom loggers, implement [interface ILogger](https://github.com/zerkalica/lom_atom/blob/master/src/interfaces.js#L9).
/**
* Atom status changes
- 'waiting' - atom fetching from server (mem.Wait throwed)
- 'proposeToReap' - atom probably will be destroyed on next tick
- 'proposeToPull' - atom will be actualized on next tick
*/
status(status: ILoggerStatus, atom: IAtom<*>): void {}
/**
* Error while actualizing atom
*/
error<V>(atom: IAtom<V>, err: Error): void {}
/**
* Atom value changed
* @param isActualize bool if true - atom handler invoked, if false - only atom.cache value getted/setted
*/
newValue<V>(atom: IAtom<V>, from?: V | Error, to: V, isActualize?: boolean): void {}
}
defaultContext.setLogger(new Logger())
```
### Map config to objects
Configs maped to object properties by class names.
Experimental feature - you can restore state on client side by providing data to class map in Injector.

@@ -684,3 +746,3 @@ ```js

[babel-plugin-transform-metadata](https://github.com/zerkalica/babel-plugin-transform-metadata) can generate displayName. To enable it, add ``` ["transform-metadata", {"addDisplayName": true}] ``` into .babelrc.
displayName in class used as a key for data mapping. [babel-plugin-transform-metadata](https://github.com/zerkalica/babel-plugin-transform-metadata) can generate displayName. To enable it, add ``` ["transform-metadata", {"addDisplayName": true}] ``` into .babelrc.

@@ -687,0 +749,0 @@ Example .babelrc:

// @flow
import {ATOM_FORCE_NONE, ATOM_FORCE_CACHE, detached} from 'lom_atom'
import type {IAtomForce} from 'lom_atom'
import {ATOM_FORCE_NONE, ATOM_FORCE_CACHE} from './interfaces'
import type {IAtomForce, DetachedDecorator} from './interfaces'
import Injector from './Injector'
import type {IFromError, IRenderFn, IReactComponent, IProvideItem, IArg, IPropsWithContext} from './interfaces'
import {renderedKey} from './interfaces'

@@ -14,7 +15,7 @@ type IAtomize<IElement, State> = (

export function createCreateElement<IElement, State>(
export function createCreateElement<IElement, State, CreateElement: Function>(
atomize: IAtomize<IElement, State>,
createElement: Function
) {
return function lomCreateElement() {
createElement: CreateElement
): CreateElement {
function lomCreateElement() {
let el = arguments[0]

@@ -81,2 +82,4 @@ let attrs = arguments[1]

}
return (lomCreateElement: any)
}

@@ -87,2 +90,3 @@

defaultFromError: IFromError<IElement>,
detached: DetachedDecorator<Object, any>,
rootInjector?: Injector = new Injector()

@@ -141,6 +145,2 @@ ): IAtomize<IElement, *> {

componentWillUnmount() {
this['AtomizedComponent.r()'].destructor()
}
destructor() {
this._el = undefined

@@ -154,2 +154,3 @@ this._keys = undefined

}
this['r()'].destructor()
}

@@ -159,4 +160,3 @@

@detached
r(element?: IElement, force?: IAtomForce): IElement {
@detached r(element?: IElement, force?: IAtomForce): IElement {
let data: IElement

@@ -172,2 +172,3 @@

data = parentContext.invokeWithProps(render.onError || defaultFromError, {error})
error[renderedKey] = true
}

@@ -174,0 +175,0 @@ parentContext = prevContext

@@ -8,2 +8,4 @@ // @flow

export {default as props} from './props'
export {default as createMobxDetached} from './createMobxDetached'
export {renderedKey} from './interfaces'

@@ -10,0 +12,0 @@ export type {

// @flow
import type {IArg, IProvideItem, IPropsWithContext, IDisposableSheet, IProcessor} from './interfaces'
import {diKey} from './interfaces'
import SheetManager from './SheetManager'

@@ -11,7 +12,7 @@ import theme from './theme'

let depId = 0
const rdiId = Symbol('rdi_id')
class Alias<T: Function> {
dest: T
constructor(dest: T) {
dest.__rdi_id = '' + ++depId
dest[rdiId] = '' + ++depId
this.dest = dest

@@ -54,7 +55,7 @@ }

} else {
if (src.__rdi_id === undefined) {
src.__rdi_id = '' + ++depId
if (src[rdiId] === undefined) {
src[rdiId] = '' + ++depId
}
const dest = item[1]
map[src.__rdi_id] = typeof dest === 'function' && !(dest instanceof Alias)
map[src[rdiId]] = typeof dest === 'function' && !(dest instanceof Alias)
? new Alias(dest)

@@ -65,6 +66,6 @@ : dest

const src = item.constructor
if (src.__rdi_id === undefined) {
src.__rdi_id = '' + ++depId
if (src[rdiId] === undefined) {
src[rdiId] = '' + ++depId
}
map[src.__rdi_id] = item
map[src[rdiId]] = item
}

@@ -84,5 +85,5 @@ }

value<V>(key: Function): V {
let id: string = key.__rdi_id
if (key.__rdi_id === undefined) {
id = key.__rdi_id = '' + ++depId
let id: string = key[rdiId]
if (key[rdiId] === undefined) {
id = key[rdiId] = '' + ++depId
}

@@ -95,3 +96,3 @@ let value = this._cache[id]

value.displayName = `${this.displayName}.${depName}`
value.__lom_di = this
value[diKey] = this
const state = this._state === undefined ? undefined : this._state[depName]

@@ -155,5 +156,5 @@ if (state && typeof state === 'object') {

if (id === undefined) {
id = key.__rdi_id
id = key[rdiId]
if (id === undefined) {
id = key.__rdi_id = '' + ++depId
id = key[rdiId] = '' + ++depId
}

@@ -160,0 +161,0 @@ }

@@ -10,5 +10,20 @@ // @flow

get?: () => T;
set?: (value: T) => void;
set?: (value: T | Error) => void;
}
export const ATOM_FORCE_NONE = 0
export const ATOM_FORCE_CACHE = 1
export const ATOM_FORCE_UPDATE = 2
export type IAtomForce = typeof ATOM_FORCE_CACHE | typeof ATOM_FORCE_UPDATE | typeof ATOM_FORCE_NONE
export type IAtomPropHandler<V> = (next?: V | Error, force?: IAtomForce, oldValue?: V) => V
export type DetachedDecoratorDescriptor<V> = TypedPropertyDescriptor<IAtomPropHandler<V>>
export type DetachedDecorator<P: Object, V> = (
proto: P,
name: string,
descr: DetachedDecoratorDescriptor<V>
) => DetachedDecoratorDescriptor<V>
export const renderedKey = Symbol('lom_rendered')
export const diKey = Symbol('rdi_di')
export type IArg = Function | {+[id: string]: Function}

@@ -15,0 +30,0 @@ export type IProvideItem = Function | Object | [Function | string, Function | mixed]

// @flow
import type {TypedPropertyDescriptor, ISheetManager, IDisposableSheet} from './interfaces'
import {diKey} from './interfaces'

@@ -33,3 +34,3 @@ let lastThemeId = 0

;(this: {
__lom_di: {
[k: typeof diKey]: {
instance: number;

@@ -43,3 +44,3 @@ }

isInstance
? `${themeId}[${this.__lom_di.instance}]`
? `${themeId}[${this[diKey].instance}]`
: themeId,

@@ -46,0 +47,0 @@ value || this[themeId](),

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc