Skip to content

Commit

Permalink
feat(react): use observable package
Browse files Browse the repository at this point in the history
  • Loading branch information
Igmat committed May 12, 2019
1 parent baeeaa6 commit ce309bd
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 90 deletions.
132 changes: 68 additions & 64 deletions packages/react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 3 additions & 6 deletions packages/react/src/ApplicationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export class ApplicationRoot extends Component<IApplicationRootProps, IApplicati
}
this.resolver.setOverrides(props.dependencies);
this.resolver.setRequirementsOverrides(props.requirements);
this.resolver.subscribeForRequirements(this.requirementsUpdate);
const hocs = this.resolver.subscribeForRequirements(this.requirementsUpdate);
this.state = { hocs };
}

render() {
Expand All @@ -90,10 +91,6 @@ export class ApplicationRoot extends Component<IApplicationRootProps, IApplicati
* @internal
*/
private requirementsUpdate = (requirements: HOC[]) => {
if (this.state) {
this.setState({ hocs: requirements });
} else {
this.state = { hocs: requirements };
}
this.setState({ hocs: requirements });
}
}
6 changes: 3 additions & 3 deletions packages/react/src/MockRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>(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)) {
Expand All @@ -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);
}
Expand Down
21 changes: 17 additions & 4 deletions packages/react/src/ResolvableComponent.ts
Original file line number Diff line number Diff line change
@@ -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<I extends IInjections = {}> =
new <P = {}, S = {}, SS = any>(props: Readonly<P>) => AbstractResolvable<I> & Component<P, S, SS>;
new <P = {}, S = {}, SS = any>(props: Readonly<P>) => AbstractResolvable<I> & React.Component<P, S, SS>;

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

/**
* @description Resolvable component factory
*/
export const ResolvableComponent = Resolvable(Component) as IResolvableComponent;
export const ResolvableComponent = (<I extends IInjections = {}, R extends IRequirements = []>(injections: I, ...requirements: R) => {
class BaseComponent<P> extends ReactComponent(injections, ...requirements)<P> {
constructor(props: Readonly<P>) {
super(props);
const originalRender = this.render.bind(this);
this.render = autorun(originalRender, this.forceUpdate.bind(this));
}
}

return BaseComponent;
}) as IComponent;
16 changes: 12 additions & 4 deletions packages/react/src/Resolver.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,7 +16,7 @@ const requirementsOverride = Symbol('Requirements Override');
* @template T type of dependency that have to be overwritten
* @template R
*/
export interface IRequirementsOverride<T extends HOC = any, R extends T = T> {
export interface IRequirementsOverride<T extends HOC = HOC, R extends T = T> {
[requirementsOverride]: [T, R];
}

Expand Down Expand Up @@ -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;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/defineRequirement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends {}>(Requirement: React.ComponentType<T>, props?: Pick<T, Exclude<keyof T, 'children'>>) {
export function defineRequirement<T extends {}>(Requirement: React.ComponentType<T>, props: T) {
return (app: ReactNode) =>
<Requirement {...props}>{app}</Requirement>;
}
7 changes: 4 additions & 3 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';

0 comments on commit ce309bd

Please sign in to comment.