diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index a17c0873..1e292d14 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -1,61 +1,31 @@ { "name": "metaf-react", - "version": "0.2.34", + "version": "0.2.35", "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/prop-types": { - "version": "15.7.1", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", - "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", - "dev": true - }, "@types/react": { - "version": "16.4.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.18.tgz", - "integrity": "sha512-eFzJKEg6pdeaukVLVZ8Xb79CTl/ysX+ExmOfAAqcFlCCK5TgFDD9kWR0S18sglQ3EmM8U+80enjUqbfnUyqpdA==", + "version": "16.8.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.17.tgz", + "integrity": "sha512-pln3mgc6VfkNg92WXODul/ONo140huK9OMsx62GlBlZ2lvjNK86PQJhYMPLO1i66aF5O9OPyZefogvNltBIszA==", "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" - } - }, - "csstype": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz", - "integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" + }, + "dependencies": { + "@types/prop-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", + "dev": true + }, + "csstype": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz", + "integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg==", + "dev": true + } } }, "react": { @@ -68,22 +38,56 @@ "object-assign": "^4.1.1", "prop-types": "^15.6.2", "scheduler": "^0.13.6" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "dev": true + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } } } diff --git a/packages/react/package.json b/packages/react/package.json index cda5e9a6..a453336b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -28,14 +28,16 @@ }, "dependencies": { "metaf-resolvable": "^0.2.33", - "react-is": "^16.5.1" + "metaf-observable": "^0.2.33", + "metaf-sync": "^0.2.33", + "react-is": "^16.8.6" }, "peerDependencies": { - "react": "^16.5.1" + "react": "^16.8.6" }, "devDependencies": { - "@types/react": "16.4.18", - "@types/react-is": "16.5.0", + "@types/react": "16.8.17", + "@types/react-is": "16.7.1", "react": "16.8.6" }, "license": "MIT" diff --git a/packages/react/src/ApplicationRoot.tsx b/packages/react/src/ApplicationRoot.tsx index 0fb5dd50..0826ecb7 100644 --- a/packages/react/src/ApplicationRoot.tsx +++ b/packages/react/src/ApplicationRoot.tsx @@ -69,7 +69,8 @@ export class ApplicationRoot extends Component { - if (this.state) { - this.setState({ hocs: requirements }); - } else { - this.state = { hocs: requirements }; - } + this.setState({ hocs: requirements }); } } diff --git a/packages/react/src/MockRoot.tsx b/packages/react/src/MockRoot.tsx index 4ff013f7..62302560 100644 --- a/packages/react/src/MockRoot.tsx +++ b/packages/react/src/MockRoot.tsx @@ -13,14 +13,14 @@ const resolveSymbol = const defaultResolver = new ReactResolver(); function addPropsToAll(children: ReactNode, resolve: MetafResolvable.ResolveForFunction) { - function recursiveClone(child: React.ReactChild): React.ReactChild { + function recursiveClone(child: ReactNode): ReactNode { if (!React.isValidElement(child)) return child; const resultingProps = typeof child.type === 'string' ? {} : { [resolveSymbol]: resolve }; let newChild = child; if (typeof child.type === 'function') { - const renderFn = (child.type.prototype.render) + const renderFn = (child.type.prototype && child.type.prototype.render) ? child.type.prototype.render // we are in class component : child.type; // we are in single function component if (!renderWrappers.has(child.type)) { @@ -38,7 +38,7 @@ function addPropsToAll(children: ReactNode, resolve: MetafResolvable.ResolveForF return render(this, ...args); }; - if (child.type.prototype.render) child.type.prototype.render = renderReplacement; + if (child.type.prototype && child.type.prototype.render) child.type.prototype.render = renderReplacement; else newChild = { ...child, type: renderReplacement }; renderWrappers.set((newChild as any).type, renderWrapper); } diff --git a/packages/react/src/ResolvableComponent.ts b/packages/react/src/ResolvableComponent.ts index df18bc8d..41e79c26 100644 --- a/packages/react/src/ResolvableComponent.ts +++ b/packages/react/src/ResolvableComponent.ts @@ -1,11 +1,12 @@ +import { autorun } from 'metaf-observable'; import { AbstractResolvable, IInjections, Resolvable } from 'metaf-resolvable'; -import { Component } from 'react'; +import React from 'react'; import { IRequirements } from './Resolver'; export type ResolvableComponent = - new

(props: Readonly

) => AbstractResolvable & Component; + new

(props: Readonly

) => AbstractResolvable & React.Component; -export interface IResolvableComponent { +export interface IComponent { /** * Resolvable component factory * @description creates new class with resolved `injections` as `dependencies` and @@ -27,7 +28,19 @@ export interface IResolvableComponent { */ (...requirements: R): ResolvableComponent; } +const ReactComponent = Resolvable(React.Component) as IComponent; + /** * @description Resolvable component factory */ -export const ResolvableComponent = Resolvable(Component) as IResolvableComponent; +export const ResolvableComponent = ((injections: I, ...requirements: R) => { + class BaseComponent

extends ReactComponent(injections, ...requirements)

{ + constructor(props: Readonly

) { + super(props); + const originalRender = this.render.bind(this); + this.render = autorun(originalRender, this.forceUpdate.bind(this)); + } + } + + return BaseComponent; +}) as IComponent; diff --git a/packages/react/src/Resolver.ts b/packages/react/src/Resolver.ts index 07e71554..5922d9c8 100644 --- a/packages/react/src/Resolver.ts +++ b/packages/react/src/Resolver.ts @@ -1,4 +1,5 @@ -import { Constructable, IDependencies, IInjections, IOverrideResult, Resolver, setResolverFn } from 'metaf-resolvable'; +import { observe } from 'metaf-observable'; +import { Constructable, IDependencies, IInjections, Injectable, IOverrideResult, Resolver, setResolverFn } from 'metaf-resolvable'; import { ReactNode } from 'react'; export type HOC = (app: ReactNode) => JSX.Element; @@ -15,7 +16,7 @@ const requirementsOverride = Symbol('Requirements Override'); * @template T type of dependency that have to be overwritten * @template R */ -export interface IRequirementsOverride { +export interface IRequirementsOverride { [requirementsOverride]: [T, R]; } @@ -75,8 +76,15 @@ export class ReactResolver extends Resolver { } subscribeForRequirements(subscriber: (requirements: HOC[]) => void) { this.requirementsSubscribers.push(subscriber); - // we also need to call subscriber with already existing requirements - subscriber(this.requirementsArray); + + return this.requirementsArray; + } + initialize(obj: Injectable) { + const result = super.initialize(obj); + + return (typeof result === 'object' && result !== null) + ? observe(result) + : result; } } diff --git a/packages/react/src/defineRequirement.tsx b/packages/react/src/defineRequirement.tsx index 0b4c5651..5986e749 100644 --- a/packages/react/src/defineRequirement.tsx +++ b/packages/react/src/defineRequirement.tsx @@ -5,10 +5,10 @@ import React, { ReactNode } from 'react'; * @description Creates requirement from particular component and props that will be passed to it * @template T type of props for this `Requirement` component * @param Requirement component type that will be used as HOC for wrapping `ApplicationRoot` - * @param [props] actual props that have to be passed to `Requirement` + * @param props actual props that have to be passed to `Requirement` * @returns requirement that could be easily used by `ResolvableComponent` or `withRequirements` HOC */ -export function defineRequirement(Requirement: React.ComponentType, props?: Pick>) { +export function defineRequirement(Requirement: React.ComponentType, props: T) { return (app: ReactNode) => {app}; } diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index fe118734..4e2eb4c9 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,6 +1,7 @@ -export { MockRoot } from './MockRoot'; -export { ApplicationRoot } from './ApplicationRoot'; -export { ResolvableComponent } from './ResolvableComponent'; export { override as dependency } from 'metaf-resolvable'; +export { ApplicationRoot } from './ApplicationRoot'; export { defineRequirement } from './defineRequirement'; +export { MockRoot } from './MockRoot'; +export { ResolvableComponent } from './ResolvableComponent'; export { overrideRequirement as requirement, resolveRequirements as withRequirements } from './Resolver'; +