diff --git a/packages/core/src/agile.ts b/packages/core/src/agile.ts index 596b576d..b1035971 100644 --- a/packages/core/src/agile.ts +++ b/packages/core/src/agile.ts @@ -110,7 +110,7 @@ export class Agile { * @param deps Array - An array of state items to depend on * @param computeFunction Function - A function where the return value is the state, ran every time a dep changes */ - public Computed = (computeFunction: () => ComputedValueType, deps?: Array) => new Computed(this, computeFunction, deps); + public Computed = (computeFunction: () => ComputedValueType, deps?: Array) => new Computed(this, computeFunction, deps?.map(state => state.observer)); //========================================================================================================= diff --git a/packages/core/src/collection/group.ts b/packages/core/src/collection/group.ts index b95f2c25..7fe1dff4 100644 --- a/packages/core/src/collection/group.ts +++ b/packages/core/src/collection/group.ts @@ -44,16 +44,16 @@ export class Group extends State> { public get output(): Array { // Add state(group) to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._output; } public get states(): Array> { // Add state(group) to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._states.map(state => state()); } @@ -225,4 +225,17 @@ export class Group extends State> { this._states = finalStates.map(state => (() => state)); this._output = finalOutput; } + + + //========================================================================================================= + // Get Public Value + //========================================================================================================= + /** + * @internal + * Returns the public value + */ + // @ts-ignore + public getPublicValue(): Array { + return this.output; + } } diff --git a/packages/core/src/collection/index.ts b/packages/core/src/collection/index.ts index f53b6472..76628c6f 100644 --- a/packages/core/src/collection/index.ts +++ b/packages/core/src/collection/index.ts @@ -373,8 +373,8 @@ export class Collection { return undefined; // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this.data[id]); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.data[id].observer); // Return data by id return this.data[id]; diff --git a/packages/core/src/computed/index.ts b/packages/core/src/computed/index.ts index 603223e7..2fd99a77 100644 --- a/packages/core/src/computed/index.ts +++ b/packages/core/src/computed/index.ts @@ -1,17 +1,18 @@ import { State, Agile, - defineConfig + defineConfig, + Observer } from '../internal'; export class Computed extends State { public agileInstance: () => Agile; public computeFunction: () => ComputedValueType; - public deps: Array = []; - public hardCodedDeps: Array = []; + public deps: Array = []; + public hardCodedDeps: Array = []; - constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, deps: Array = []) { + constructor(agileInstance: Agile, computeFunction: () => ComputedValueType, deps: Array = []) { super(agileInstance, computeFunction()); this.agileInstance = () => agileInstance; this.computeFunction = computeFunction; @@ -29,8 +30,8 @@ export class Computed extends State // Note can't use 'super.value' because of 'https://github.com/Microsoft/TypeScript/issues/338' // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._value; } @@ -62,7 +63,7 @@ export class Computed extends State */ public updateComputeFunction(computeFunction: () => ComputedValueType, deps: Array = [], options?: { background?: boolean, sideEffects?: boolean }) { this.computeFunction = computeFunction; - this.hardCodedDeps = deps; + this.hardCodedDeps = deps.map(state => state.observer); // Recompute for setting initial state value and adding missing dependencies this.recompute(options); @@ -78,28 +79,30 @@ export class Computed extends State */ public computeValue(): ComputedValueType { // Set tracking state to true which will than track all states which for instance call state.value - this.agileInstance().runtime.trackState = true; + this.agileInstance().runtime.trackObserver = true; // Call computeFunction const computedValue = this.computeFunction(); // Get tracked states and set trackSate to false - let foundStates = this.agileInstance().runtime.getTrackedStates(); + let foundObservers = this.agileInstance().runtime.getTrackedObservers(); // Handle foundStates dependencies - const newDeps: Array = []; - foundStates.forEach(state => { + const newDeps: Array = []; + foundObservers.forEach(observer => { + if(!observer) return; + // Add the state to newDeps - newDeps.push(state); + newDeps.push(observer); // Add this as dependency of the state - state.dep.depend(this); + observer.dep.depend(this.observer); }); // Handle hardCoded dependencies - this.hardCodedDeps.forEach(state => { + this.hardCodedDeps.forEach(observer => { // Add this as dependency of the state - state.dep.depend(this); + observer.dep.depend(this.observer); }); // Set deps diff --git a/packages/core/src/event/index.ts b/packages/core/src/event/index.ts index 1a4435ea..53a7e502 100644 --- a/packages/core/src/event/index.ts +++ b/packages/core/src/event/index.ts @@ -195,5 +195,4 @@ export class Event { return; } - } diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index b6b85bc0..68eb523a 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -8,9 +8,20 @@ // Agile export * from './agile'; +// Runtime +export * from './runtime'; +export * from './runtime/observer'; +export * from './runtime/dep'; +export * from './runtime/job'; +export * from './runtime/subscription/SubscriptionContainer'; +export * from './runtime/subscription/CallbackSubscriptionContainer'; +export * from './runtime/subscription/ComponentSubscriptionContainer'; +export * from './runtime/subscription/sub'; + + // State export * from './state'; -export * from './state/dep'; +export * from './state/state.observer'; // Computed export {Computed} from './computed'; @@ -25,12 +36,8 @@ export * from './collection/selector'; export * from './event'; // Internal Classes -export * from './runtime'; export * from './storage'; export * from './integrations'; -export * from './runtime/subscription/sub'; -export * from './runtime/subscription/CallbackSubscriptionContainer'; -export * from './runtime/subscription/ComponentSubscriptionContainer'; // Utils export * from './utils'; \ No newline at end of file diff --git a/packages/core/src/state/dep.ts b/packages/core/src/runtime/dep.ts similarity index 71% rename from packages/core/src/state/dep.ts rename to packages/core/src/runtime/dep.ts index 29abe323..dc119572 100644 --- a/packages/core/src/state/dep.ts +++ b/packages/core/src/runtime/dep.ts @@ -1,17 +1,17 @@ import { - State, - SubscriptionContainer + SubscriptionContainer, + Observer } from '../internal'; export class Dep { - public deps: Set = new Set(); // Dependencies from the State + public deps: Set = new Set(); // Dependencies from the State public subs: Set = new Set(); // Subscriptions (for instance a component subscribes to a state to get rerendered if the state changes) - constructor(initialDeps?: Array) { + constructor(initialDeps?: Array) { if (!initialDeps) return; // Add Initial Dependencies to Deps - initialDeps.forEach(dep => this.deps.add(dep)); + initialDeps.forEach(observable => this.deps.add(observable)); } @@ -19,11 +19,11 @@ export class Dep { // Depend //========================================================================================================= /** - * Add new Dependency to the State + * Add new Dependency to the Observer */ - public depend(state: State) { - if (state.dep !== this && !this.deps.has(state)) - this.deps.add(state); + public depend(observable: Observer) { + if (observable.dep !== this && !this.deps.has(observable)) + this.deps.add(observable); } @@ -31,7 +31,7 @@ export class Dep { // Subscribe //========================================================================================================= /** - * Add new Subscription to the State + * Add new Subscription to the Observer */ public subscribe(subscriptionContainer: SubscriptionContainer){ if(!this.subs.has(subscriptionContainer)) @@ -43,10 +43,10 @@ export class Dep { // Unsubscribe //========================================================================================================= /** - * Delete Subscription from the State + * Delete Subscription from the Observer */ public unsubscribe(subscriptionContainer: SubscriptionContainer){ - if(!this.subs.has(subscriptionContainer)) + if(this.subs.has(subscriptionContainer)) this.subs.delete(subscriptionContainer); } } diff --git a/packages/core/src/runtime/index.ts b/packages/core/src/runtime/index.ts index 13d82622..f36a74d9 100644 --- a/packages/core/src/runtime/index.ts +++ b/packages/core/src/runtime/index.ts @@ -1,60 +1,32 @@ import { - State, Agile, - Computed, SubscriptionContainer, - copy, - defineConfig + defineConfig, + Observer, + Job, + JobConfigInterface, + CallbackSubscriptionContainer, ComponentSubscriptionContainer, StateObserver } from '../internal'; -import {ComponentSubscriptionContainer} from "./subscription/ComponentSubscriptionContainer"; -import {CallbackSubscriptionContainer} from "./subscription/CallbackSubscriptionContainer"; - -export interface JobInterface { - state: State - newStateValue?: any - options?: { - background?: boolean - sideEffects?: boolean - forceRerender?: boolean - } -} - -export interface JobConfigInterface { - perform?: boolean // Should preform the job instantly - background?: boolean // Shouldn't cause an rerender during the perform process - sideEffects?: boolean // Should perform sideEffects like rebuilding groups - forceRerender?: boolean // Force rerender although for instance the values are the same -} export class Runtime { + public agileInstance: () => Agile; // Queue system - private currentJob: JobInterface | null = null; - private jobQueue: Array = []; - private jobsToRerender: Array = []; + private currentJob: Job | null = null; + private jobQueue: Array = []; + private notReadyJobsToRerender: Array = []; + private jobsToRerender: Array = []; // Used for tracking computed dependencies - public trackState: boolean = false; // Check if agile should track states - public foundStates: Set = new Set(); // States which were tracked during the track time - - public internalIngestKey = "This is an Internal Key for ingesting internal stuff!"; + public trackObserver: boolean = false; // Check if agile should track states + public foundObservers: Set = new Set(); // States which were tracked during the track time constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; } - - //========================================================================================================= - // Ingest - //========================================================================================================= - /** - * @internal - * Creates a Job out of State and new Value and than add it to a job queue - * Note: its not possible to set a state to undefined because undefined is used for internal activities! - */ - public ingest(state: State, newStateValue?: any, options: JobConfigInterface = {}): void { - // Merge default values into options + public ingest(observer: Observer, options: JobConfigInterface): void { options = defineConfig(options, { perform: true, background: false, @@ -63,39 +35,20 @@ export class Runtime { }); // Create Job - const job: JobInterface = { - state: state, - newStateValue: newStateValue, - options: { - background: options.background, - sideEffects: options.sideEffects, - forceRerender: options.forceRerender - } - }; - - // Grab nextState if newState not passed or compute if needed - if (newStateValue === this.internalIngestKey) { - if (job.state instanceof Computed) - job.newStateValue = job.state.computeValue(); - else - job.newStateValue = job.state.nextState - } - - // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) - if (JSON.stringify(state.value) === JSON.stringify(job.newStateValue) && !options.forceRerender) { - if (this.agileInstance().config.logJobs) - console.warn("Agile: Doesn't perform job because state values are the same! ", job); - return; - } + const job = new Job(observer, { + background: options.background, + sideEffects: options.sideEffects, + forceRerender: options.forceRerender + }); // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Created Job(${job.state.key})`, job); + console.log(`Agile: Created Job(${job.observer.key})`, job); - // Push the Job to the Queue (safety.. that no Job get forgotten) + // Push the Job to the Queue this.jobQueue.push(job); - // Perform the Job + // Perform the Job and remove it from queue if (options.perform) { const performJob = this.jobQueue.shift(); if (performJob) @@ -113,28 +66,15 @@ export class Runtime { * @internal * Perform a State Update */ - private perform(job: JobInterface): void { - // Set Job to currentJob + private perform(job: Job): void { this.currentJob = job; - // Set Previous State - job.state.previousState = copy(job.state.value); - - // Write new value into the State - job.state.privateWrite(job.newStateValue); - - // Set isSet - job.state.isSet = job.newStateValue !== job.state.initialState; + // Perform Job + job.observer.perform(job); + job.performed = true; - // Set is placeholder to false, because it has got a value - if (job.state.isPlaceholder) - job.state.isPlaceholder = false; - - // Perform SideEffects like watcher functions or state.sideEffects - this.sideEffects(job); - - // Set Job as completed (The deps and subs of completed jobs will be updated) - if (!job.options?.background || job.options?.forceRerender) + // Add to rerender + if (job.rerender) this.jobsToRerender.push(job); // Reset Current Job @@ -142,101 +82,59 @@ export class Runtime { // Logging if (this.agileInstance().config.logJobs) - console.log(`Agile: Completed Job(${job.state.key})`, job); + console.log(`Agile: Completed Job(${job.observer.key})`, job); // Continue the Loop and perform the next job.. if no job is left update the Subscribers for each completed job if (this.jobQueue.length > 0) { const performJob = this.jobQueue.shift(); if (performJob) this.perform(performJob); - else - console.warn("Agile: Failed to perform Job ", job); } else { // https://stackoverflow.com/questions/9083594/call-settimeout-without-delay setTimeout(() => { - // Cause rerender on Subscribers this.updateSubscribers(); - }) + }); } } - //========================================================================================================= - // Side Effect - //========================================================================================================= - /** - * @internal - * SideEffects are sideEffects of the perform function.. for instance the watchers - */ - private sideEffects(job: JobInterface) { - const state = job.state; - - // Call Watchers - for (let watcher in state.watchers) - if (typeof state.watchers[watcher] === 'function') - state.watchers[watcher](state.getPublicValue()); - - // Call State SideEffects - if (typeof state.sideEffects === 'function' && job.options?.sideEffects) - state.sideEffects(); - - // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) - state.dep.deps.forEach((state) => this.ingest(state, this.internalIngestKey, {perform: false})); - } - - //========================================================================================================= // Update Subscribers //========================================================================================================= /** * @internal - * This will be update all Subscribers of complete jobs + * Updates all Subscribers */ private updateSubscribers(): void { - // Check if Agile has an integration because its useless to go trough this process without framework - // It won't happen anything because the state has no subs.. but this check here will maybe improve the performance - if (!this.agileInstance().integrations.hasIntegration()) { - this.jobsToRerender = []; - return; - } - - // Subscriptions that has to be updated (Set = For preventing double subscriptions without further checks) + // Subscriptions that has to be updated const subscriptionsToUpdate: Set = new Set(); // Map through Jobs to Rerender - this.jobsToRerender.forEach(job => - // Map through subs of the current Job State - job.state.dep.subs.forEach(subscriptionContainer => { + this.jobsToRerender.concat(this.notReadyJobsToRerender).forEach(job => + job.observer.dep.subs.forEach(subscriptionContainer => { // Check if subscriptionContainer is ready - if (!subscriptionContainer.ready) - console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); + if (!subscriptionContainer.ready) { + this.notReadyJobsToRerender.push(job); + if (this.agileInstance().config.logJobs) + console.warn("Agile: SubscriptionContainer isn't ready yet ", subscriptionContainer); + return; + } // For a Container that require props to be passed - if (subscriptionContainer.passProps) { - let localKey: string | null = null; + if (subscriptionContainer.passProps) + this.handlePassProps(subscriptionContainer, job); - // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object - for (let key in subscriptionContainer.propStates) - if (subscriptionContainer.propStates[key] === job.state) - localKey = key; - - // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object - if (localKey) - subscriptionContainer.propKeysChanged.push(localKey); - } subscriptionsToUpdate.add(subscriptionContainer); - })); + }) + ); - // Perform Component or Callback updates + // Rerender the Components via CallbackSubscriptions or ComponentSubscription subscriptionsToUpdate.forEach(subscriptionContainer => { - // If Callback based subscription call the Callback Function - if (subscriptionContainer instanceof CallbackSubscriptionContainer) { + if (subscriptionContainer instanceof CallbackSubscriptionContainer) subscriptionContainer.callback(); - return; - } - // If Component based subscription call the updateMethod - this.agileInstance().integrations.update(subscriptionContainer.component, this.formatChangedPropKeys(subscriptionContainer)); + if (subscriptionContainer instanceof ComponentSubscriptionContainer) + this.agileInstance().integrations.update(subscriptionContainer.component, this.formatChangedPropKeys(subscriptionContainer)); }); // Log Job @@ -248,6 +146,26 @@ export class Runtime { } + //========================================================================================================= + // Handle Pass Props + //========================================================================================================= + /** + * @internal + * Handle prop passing subscription + */ + public handlePassProps(subscriptionContainer: SubscriptionContainer, job: Job) { + let localKey: string | null = null; + + // Find the local Key for this update by comparing the State instance from this Job to the State instances in the propStates object + for (let key in subscriptionContainer.subs) + if (subscriptionContainer.subs[key] === job.observer) + localKey = key; + + // If matching key is found push it into the SubscriptionContainer propKeysChanged where it later will be build to an changed prop object + if (localKey) + subscriptionContainer.propKeysChanged.push(localKey); + } + //========================================================================================================= // Format Changed Prop Keys //========================================================================================================= @@ -260,8 +178,8 @@ export class Runtime { // Build Object subscriptionContainer.propKeysChanged.forEach(changedKey => { - if (subscriptionContainer.propStates) - finalObject[changedKey] = subscriptionContainer.propStates[changedKey].value; + if (subscriptionContainer.subs[changedKey] instanceof StateObserver) + finalObject[changedKey] = subscriptionContainer.subs[changedKey].value; }); return finalObject; @@ -275,13 +193,13 @@ export class Runtime { * @internal * Will return all tracked States */ - public getTrackedStates() { - const finalFoundStates = this.foundStates; + public getTrackedObservers(): Set { + const finalFoundObservers = this.foundObservers; // Reset tracking - this.trackState = false; - this.foundStates = new Set(); + this.trackObserver = false; + this.foundObservers = new Set(); - return finalFoundStates; + return finalFoundObservers; } } diff --git a/packages/core/src/runtime/job.ts b/packages/core/src/runtime/job.ts new file mode 100644 index 00000000..557388d8 --- /dev/null +++ b/packages/core/src/runtime/job.ts @@ -0,0 +1,30 @@ +import {Observer, defineConfig} from "../internal"; + +export interface JobConfigInterface { + background?: boolean // If it should cause an rerender + sideEffects?: boolean // If it should call sideEffects + forceRerender?: boolean // Force a rerender + perform?: boolean // If the Job should be performed +} + +export class Job { + + public observer: ObserverType; + public config: JobConfigInterface; + public rerender: boolean; + public performed: boolean = false; + + constructor(observer: ObserverType, config: JobConfigInterface) { + this.config = defineConfig(config, { + background: false, + sideEffects: true, + forceRerender: false + }); + + this.observer = observer; + this.config = config; + + // @ts-ignore + this.rerender = (!config.background || config.forceRerender) && this.observer.agileInstance().integrations.hasIntegration(); + } +} \ No newline at end of file diff --git a/packages/core/src/runtime/observer.ts b/packages/core/src/runtime/observer.ts new file mode 100644 index 00000000..0a2f3ad8 --- /dev/null +++ b/packages/core/src/runtime/observer.ts @@ -0,0 +1,29 @@ +import {Agile, Dep, StateKey, Job} from "../internal"; + +export type ObserverKey = string | number; + +export class Observer { + + public agileInstance: () => Agile; + + public _key?: ObserverKey; + public dep: Dep; // Dependencies and Subscriptions of the Observer + + constructor(agileInstance: Agile, deps?: Array, key?: ObserverKey, value?: ValueType) { + this.agileInstance = () => agileInstance; + this.dep = new Dep(deps); + this._key = key; + } + + public set key(value: StateKey | undefined) { + this._key = value; + } + + public get key(): StateKey | undefined { + return this._key; + } + + public perform(job: Job) { + console.warn("Didn't set perform function in Observer ", this.key); + } +} \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts index 4f2868bf..ae1852b3 100644 --- a/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/CallbackSubscriptionContainer.ts @@ -1,11 +1,10 @@ -import {State} from "../../internal"; -import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; +import {Observer, SubscriptionContainer} from "../../internal"; -export class CallbackSubscriptionContainer extends ComponentSubscriptionContainer { +export class CallbackSubscriptionContainer extends SubscriptionContainer{ public callback: Function; - constructor(callback: Function, subs?: Set) { - super(null, subs); + constructor(callback: Function, subs?: Set) { + super(subs); this.callback = callback; } diff --git a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts index 36e1fc74..0af4daff 100644 --- a/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts +++ b/packages/core/src/runtime/subscription/ComponentSubscriptionContainer.ts @@ -1,19 +1,10 @@ -import {State} from "../../internal"; +import {Observer, SubscriptionContainer} from "../../internal"; -export class ComponentSubscriptionContainer { +export class ComponentSubscriptionContainer extends SubscriptionContainer{ public component: any; - // Only needed by object orientated subscriptions - public passProps: boolean = false; - public propStates?: { [key: string]: State }; // states which will than be returned as prop object by the integration - public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update - - public ready: boolean = false; - public subs: Set = new Set([]); // States that are subscribed by this component - - constructor(component: any, subs?: Set) { + constructor(component: any, subs?: Set) { + super(subs); this.component = component - if (subs) - this.subs = subs; } } \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/SubscriptionContainer.ts b/packages/core/src/runtime/subscription/SubscriptionContainer.ts new file mode 100644 index 00000000..53b9ce25 --- /dev/null +++ b/packages/core/src/runtime/subscription/SubscriptionContainer.ts @@ -0,0 +1,15 @@ +import {Observer} from "../../internal"; + +export class SubscriptionContainer { + + // For Object orientated subscriptions + public passProps: boolean = false; + public propKeysChanged: Array = []; // Used to preserve local keys to update before update is performed, cleared every update + + public ready: boolean = false; + public subs: Set = new Set([]); // Observers that are Subscribed to this Subscription Container + + constructor(subs?: Set) { + if (subs) this.subs = subs; + } +} \ No newline at end of file diff --git a/packages/core/src/runtime/subscription/sub.ts b/packages/core/src/runtime/subscription/sub.ts index 7cb8137a..1c8cd2bf 100644 --- a/packages/core/src/runtime/subscription/sub.ts +++ b/packages/core/src/runtime/subscription/sub.ts @@ -1,14 +1,4 @@ -import {Agile, State} from '../../internal'; -import {ComponentSubscriptionContainer} from "./ComponentSubscriptionContainer"; -import {CallbackSubscriptionContainer} from "./CallbackSubscriptionContainer"; - - -//========================================================================================================= -// Subscription Container -//========================================================================================================= - -export type SubscriptionContainer = ComponentSubscriptionContainer | CallbackSubscriptionContainer; - +import {Agile, Observer, StateObserver, SubscriptionContainer, ComponentSubscriptionContainer, CallbackSubscriptionContainer} from '../../internal'; //========================================================================================================= // Controller @@ -18,10 +8,10 @@ export class SubController { public agileInstance: () => Agile; // Component based Subscription - public components: Set = new Set(); + public componentSubs: Set = new Set(); // Callback based Subscription - public callbacks: Set = new Set(); + public callbackSubs: Set = new Set(); public constructor(agileInstance: Agile) { this.agileInstance = () => agileInstance; @@ -34,26 +24,28 @@ export class SubController { /** * Subscribe to Agile State with a returned object of props this props can than be returned by the component (See react-integration) */ - public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: State } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: State['value'] } } { + public subscribeWithSubsObject(subscriptionInstance: any, subs: { [key: string]: Observer } = {}): { subscriptionContainer: SubscriptionContainer, props: { [key: string]: StateObserver['value'] } } { const subscriptionContainer = this.registerSubscription(subscriptionInstance); - const props: { [key: string]: State } = {}; + const props: { [key: string]: Observer } = {}; subscriptionContainer.passProps = true; - subscriptionContainer.propStates = {...subs}; // Go through subs let localKeys = Object.keys(subs); localKeys.forEach(key => { - const state = subs[key]; + const observable = subs[key]; // Add State to SubscriptionContainer Subs - subscriptionContainer.subs.add(state); + subscriptionContainer.subs.add(observable); // Add SubscriptionContainer to State Subs - state.dep.subscribe(subscriptionContainer); + observable.dep.subscribe(subscriptionContainer); + + // If no state instance return, because only states have a 'value' + if(!(observable instanceof StateObserver)) return; // Add state to props - props[key] = state.value; + props[key] = observable.value; }); return { @@ -69,15 +61,15 @@ export class SubController { /** * Subscribe to Agile State */ - public subscribeWithSubsArray(subscriptionInstance: any, subs: Array = []): SubscriptionContainer { + public subscribeWithSubsArray(subscriptionInstance: any, subs: Array = []): SubscriptionContainer { const subscriptionContainer = this.registerSubscription(subscriptionInstance, subs); - subs.forEach(state => { + subs.forEach(observable => { // Add State to SubscriptionContainer Subs - subscriptionContainer.subs.add(state); + subscriptionContainer.subs.add(observable); - // Add SubscriptionContainer to State Dependencies Subs - state.dep.subscribe(subscriptionContainer); + // Add SubscriptionContainer to State Subs + observable.dep.subscribe(subscriptionContainer); }); return subscriptionContainer; @@ -116,7 +108,7 @@ export class SubController { /** * Registers the Component/Callback Subscription and returns a SubscriptionContainer */ - public registerSubscription(integrationInstance: any, subs: Array = []): SubscriptionContainer { + public registerSubscription(integrationInstance: any, subs: Array = []): SubscriptionContainer { if (typeof integrationInstance === 'function') return this.registerCallbackSubscription(integrationInstance, subs); @@ -124,20 +116,6 @@ export class SubController { } - //========================================================================================================= - // Mount - //========================================================================================================= - /** - * This will mount the component (Mounts currently only useful in Component based Subscription) - */ - public mount(integrationInstance: any) { - if (!integrationInstance.componentContainer) return; - - // Set Ready to true - integrationInstance.componentContainer.ready = true; - } - - //========================================================================================================= // Register Component Subscription //========================================================================================================= @@ -145,7 +123,7 @@ export class SubController { * Registers Component Subscription * Note: Helper Function */ - private registerComponentSubscription(componentInstance: any, subs: Array = []): ComponentSubscriptionContainer { + private registerComponentSubscription(componentInstance: any, subs: Array = []): ComponentSubscriptionContainer { // Create ComponentSubscriptionContainer const componentContainer = new ComponentSubscriptionContainer(componentInstance, new Set(subs)); @@ -154,7 +132,7 @@ export class SubController { componentInstance.componentSubscriptionContainer = componentContainer; // Add to components - this.components.add(componentContainer); + this.componentSubs.add(componentContainer); // Set Ready if (!this.agileInstance().config.waitForMount) @@ -174,12 +152,12 @@ export class SubController { * Registers Callback Subscription * Note: Helper Function */ - private registerCallbackSubscription(callbackFunction: () => void, subs: Array = []): CallbackSubscriptionContainer { + private registerCallbackSubscription(callbackFunction: () => void, subs: Array = []): CallbackSubscriptionContainer { // Create CallbackSubscriptionContainer const callbackContainer = new CallbackSubscriptionContainer(callbackFunction as Function, new Set(subs)); // Add to callbacks - this.callbacks.add(callbackContainer); + this.callbackSubs.add(callbackContainer); // Set Ready callbackContainer.ready = true; @@ -189,4 +167,17 @@ export class SubController { return callbackContainer; } + + + + //========================================================================================================= + // Mount + //========================================================================================================= + /** + * This will mount the component (Mounts currently only useful in Component based Subscription) + */ + public mount(integrationInstance: any) { + if (!integrationInstance.componentContainer) return; + integrationInstance.componentContainer.ready = true; + } } diff --git a/packages/core/src/state/index.ts b/packages/core/src/state/index.ts index cf0d860d..91038070 100644 --- a/packages/core/src/state/index.ts +++ b/packages/core/src/state/index.ts @@ -1,18 +1,17 @@ import { Agile, - Dep, StorageKey, copy, defineConfig, flatMerge, - isValidObject + isValidObject, + StateObserver, internalIngestKey } from '../internal'; import {persistValue, updateValue} from './persist'; - export type StateKey = string | number; -export interface PersistSettingsInterface { +export interface PersistConfigInterface { isPersisted: boolean // Is State persisted persistKey?: string | number // Current Persist Key.. for handling twice persisted states } @@ -22,30 +21,29 @@ export class State { public _key?: StateKey; // should be a unique key/name which identifies the state public valueType?: string; // primitive types for js users - public dep: Dep; // Includes the subscriptions and dependencies of the state public watchers: { [key: string]: (value: any) => void } = {}; public sideEffects?: Function; // SideEffects can be set by extended classes, such as Groups to build their output. public isSet: boolean = false; // Has been changed from initial value - public persistSettings: PersistSettingsInterface; // Includes persist 'settings' (have to rename if I got an better name) - public output?: any; // This contains the public value.. if _value doesn't contain the public value (Used for example by collections) + public persistConfig: PersistConfigInterface; // Includes persist 'settings' (have to rename if I got an better name) public isPlaceholder: boolean = false; // Defines if the state is a placeholder or not + public observer: StateObserver; public initialState: ValueType; public _value: ValueType; // The current value of the state public previousState: ValueType; // Will be set in runtime public nextState: ValueType; // The next state is used internal and represents the nextState which can be edited as wished (cleaner than always setting the state) - constructor(agileInstance: Agile, initialState: ValueType, key?: StateKey, deps: Array = []) { + constructor(agileInstance: Agile, initialState: ValueType, key?: StateKey, deps: Array = []) { this.agileInstance = () => agileInstance; this.initialState = initialState; - this.dep = new Dep(deps); this._key = key; this._value = initialState; this.previousState = initialState; this.nextState = initialState; - this.persistSettings = { + this.persistConfig = { isPersisted: false } + this.observer = new StateObserver(agileInstance, this, deps.map(state => state.observer), key); } public set value(value: ValueType) { @@ -54,14 +52,15 @@ export class State { public get value(): ValueType { // Add state to foundState (for auto tracking used states in computed functions) - if (this.agileInstance().runtime.trackState) - this.agileInstance().runtime.foundStates.add(this); + if (this.agileInstance().runtime.trackObserver) + this.agileInstance().runtime.foundObservers.add(this.observer); return this._value; } public set key(value: StateKey | undefined) { this._key = value; + this.observer.key = `o_${value}`; } public get key(): StateKey | undefined { @@ -93,7 +92,7 @@ export class State { return this; // Ingest updated value - this.agileInstance().runtime.ingest(this, value, { + this.observer.ingest(value, { background: options.background, sideEffects: options.sideEffects }); @@ -117,11 +116,7 @@ export class State { forceRerender: false }); - this.agileInstance().runtime.ingest(this, this.agileInstance().runtime.internalIngestKey, { - background: options.background, - sideEffects: options.sideEffects, - forceRerender: options.forceRerender - }); + this.observer.ingest(internalIngestKey, options); } @@ -167,8 +162,8 @@ export class State { */ public reset(): this { // Remove State from Storage (because it is than the initial State again and there is no need to save it anymore) - if (this.persistSettings.isPersisted && this.persistSettings.persistKey) - this.agileInstance().storage.remove(this.persistSettings.persistKey); + if (this.persistConfig.isPersisted && this.persistConfig.persistKey) + this.agileInstance().storage.remove(this.persistConfig.persistKey); // Set State to initial State this.set(this.initialState); @@ -291,11 +286,9 @@ export class State { //========================================================================================================= /** * @internal - * Returns 100% the public value of a state because at some points (group) the _value contains only keys + * Returns the public value (will be overwritten for instance in group) */ public getPublicValue(): ValueType { - if (this.output !== undefined) - return this.output; return this._value; } diff --git a/packages/core/src/state/persist.ts b/packages/core/src/state/persist.ts index bbd23ba2..acf7fe89 100644 --- a/packages/core/src/state/persist.ts +++ b/packages/core/src/state/persist.ts @@ -24,8 +24,8 @@ export async function persistValue(state: State, key?: StorageKey) { const storage = state.agileInstance().storage; // Check if persist State is already a isPersistState if so remove the old one - if (state.persistSettings.isPersisted && state.persistSettings.persistKey) - storage.remove(state.persistSettings.persistKey); + if (state.persistConfig.isPersisted && state.persistConfig.persistKey) + storage.remove(state.persistConfig.persistKey); // Add State to persistedStates in Storage storage.persistedStates.add(state); @@ -34,7 +34,7 @@ export async function persistValue(state: State, key?: StorageKey) { await handleStorageValue(key, storage, state); // Set persistSettings - state.persistSettings = { + state.persistConfig = { isPersisted: true, persistKey: key } @@ -48,8 +48,8 @@ export async function persistValue(state: State, key?: StorageKey) { * Save current _value into storage if isPersistState */ export function updateValue(state: State) { - if (state.persistSettings.isPersisted && state.persistSettings.persistKey) - state.agileInstance().storage.set(state.persistSettings.persistKey, state._value); + if (state.persistConfig.isPersisted && state.persistConfig.persistKey) + state.agileInstance().storage.set(state.persistConfig.persistKey, state._value); } diff --git a/packages/core/src/state/state.observer.ts b/packages/core/src/state/state.observer.ts new file mode 100644 index 00000000..87fdef58 --- /dev/null +++ b/packages/core/src/state/state.observer.ts @@ -0,0 +1,122 @@ +import { + Agile, + Observer, + State, + Computed, + Job, + JobConfigInterface, + copy, + defineConfig, + ObserverKey +} from '../internal'; + +export const internalIngestKey = "THIS_IS_AN_INTERNAL_KEY_FOR_INGESTING_INTERNAL_STUFF"; + +export class StateObserver extends Observer { + + public state: () => State; // State on which the Observer is watching + public nextStateValue: ValueType; // The next State value + public value: ValueType; + + constructor(agileInstance: Agile, state: State, deps?: Array, key?: ObserverKey) { + super(agileInstance, deps, key); + this.state = () => state; + this.nextStateValue = state.value; + this.value = state.value; + } + + + //========================================================================================================= + // Ingest + //========================================================================================================= + /** + * @internal + * Creates a Job out of State and new Value and than add it to a job queue + * Note: its not possible to set a state to undefined because undefined is used for internal activities! + */ + public ingest(newStateValue?: any, options: JobConfigInterface = {}): void { + // Merge default values into options + options = defineConfig(options, { + perform: true, + background: false, + sideEffects: true, + forceRerender: false + }); + + // Grab nextState if newState not passed or compute if needed + if (newStateValue === internalIngestKey) { + if (this.state() instanceof Computed) + // @ts-ignore + this.nextStateValue = this.state().computeValue(); + else + this.nextStateValue = this.state().nextState + } else + this.nextStateValue = newStateValue; + + // Check if state value und newStateValue are the same.. if so return except force Rerender (stringifying because of possible object or array) + if (JSON.stringify(this.state().value) === JSON.stringify(this.nextStateValue) && !options.forceRerender) { + if (this.agileInstance().config.logJobs) + console.warn("Agile: Doesn't created job because state values are the same! "); + return; + } + + // Ingest into runtime + this.agileInstance().runtime.ingest(this, options); + } + + + //========================================================================================================= + // Perform + //========================================================================================================= + /** + * @internal + * TOD_O + */ + public perform(job: Job) { + const state = job.observer.state(); + + // Set Previous State + state.previousState = copy(state.value); + + // Write new value into the State + state.privateWrite(this.nextStateValue); + + // Set isSet + state.isSet = this.nextStateValue !== state.initialState; + + // Set is placeholder to false, because it has got a value + if (state.isPlaceholder) + state.isPlaceholder = false; + + // Set Observer value + this.value = this.nextStateValue; + + // Perform SideEffects like watcher functions or state.sideEffects + this.sideEffects(job); + } + + + //========================================================================================================= + // Side Effect + //========================================================================================================= + /** + * @internal + * SideEffects are sideEffects of the perform function.. for instance the watchers + */ + private sideEffects(job: Job) { + const state = job.observer.state(); + + // Call Watchers + for (let watcher in state.watchers) + if (typeof state.watchers[watcher] === 'function') + state.watchers[watcher](state.getPublicValue()); + + // Call State SideEffects + if (typeof state.sideEffects === 'function' && job.config?.sideEffects) + state.sideEffects(); + + + // Ingest Dependencies of State (Perform is false because it will be performed anyway after this sideEffect) + job.observer.dep.deps.forEach((observer) => observer instanceof StateObserver && observer.ingest(internalIngestKey, {perform: false})); + } +} \ No newline at end of file diff --git a/packages/core/tests/collection/functions/collect.function.spec.ts b/packages/core/tests/collection/functions/collect.function.spec.ts index deea14f7..e66c88aa 100644 --- a/packages/core/tests/collection/functions/collect.function.spec.ts +++ b/packages/core/tests/collection/functions/collect.function.spec.ts @@ -28,7 +28,7 @@ describe('Collect Function Tests', () => { it('Has correct initial values', () => { expect(JSON.stringify(MY_COLLECTION.data)).to.eq(JSON.stringify({}), 'MY_COLLECTION has correct data'); expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(myHookCollection)).to.eq(JSON.stringify([]), 'myHookState has correct MY_COLLECTION value'); expect(rerenderCount).to.eq(0, 'rerenderCount is 0'); @@ -311,7 +311,7 @@ describe('Collect Function Tests', () => { it('Has correct initial values', () => { expect(JSON.stringify(MY_COLLECTION.data)).to.eq(JSON.stringify({}), 'MY_COLLECTION has correct data'); expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(myHookCollection)).to.eq(JSON.stringify([]), 'myHookState has correct MY_COLLECTION value'); }); diff --git a/packages/core/tests/collection/functions/update.function.spec.ts b/packages/core/tests/collection/functions/update.function.spec.ts index 6798d92b..94096012 100644 --- a/packages/core/tests/collection/functions/update.function.spec.ts +++ b/packages/core/tests/collection/functions/update.function.spec.ts @@ -29,7 +29,7 @@ describe('Update Function Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created') - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(MY_COLLECTION.size).to.eq(3, 'MY_COLLECTION has correct size'); expect(JSON.stringify(MY_COLLECTION.data[1].value)).to.eq(JSON.stringify({ id: 1, diff --git a/packages/core/tests/collection/group/default.spec.ts b/packages/core/tests/collection/group/default.spec.ts index afc85def..2e1f87db 100644 --- a/packages/core/tests/collection/group/default.spec.ts +++ b/packages/core/tests/collection/group/default.spec.ts @@ -31,14 +31,14 @@ describe('Default Group Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created'); - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['default'].value)).to.eq(JSON.stringify([]), 'default Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['default'].output)).to.eq(JSON.stringify([]), 'default Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['default'].states)).to.eq(JSON.stringify([]), 'default Group has correct initial states'); expect(MY_COLLECTION.groups['default'].key).to.eq('default', 'group1 Group has correct initial key'); expect(MY_COLLECTION.groups['group1'] instanceof Group).to.eq(true, 'MY_COLLECTION group1 Group has been created'); - expect(MY_COLLECTION.groups['group1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); + expect(MY_COLLECTION.groups['group1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].value)).to.eq(JSON.stringify([]), 'group1 Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].output)).to.eq(JSON.stringify([]), 'group1 Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].states)).to.eq(JSON.stringify([]), 'group1 Group has correct initial states'); @@ -99,14 +99,14 @@ describe('Default Group Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.groups['default'] instanceof Group).to.eq(true, 'MY_COLLECTION default Group has been created'); - expect(MY_COLLECTION.groups['default']?.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); + expect(MY_COLLECTION.groups['default']?.observer.dep.subs.size === 0).to.eq(true, 'MY_COLLECTION default Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['default'].value)).to.eq(JSON.stringify([]), 'default has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['default'].output)).to.eq(JSON.stringify([]), 'default has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['default'].states)).to.eq(JSON.stringify([]), 'default has correct initial states'); expect(MY_COLLECTION.groups['default'].key).to.eq('default', 'default Group has correct initial key'); expect(MY_COLLECTION.groups['group1'] instanceof Group).to.eq(true, 'MY_COLLECTION group1 Group has been created'); - expect(MY_COLLECTION.groups['group1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); + expect(MY_COLLECTION.groups['group1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION group1 Group has correct subs size'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].value)).to.eq(JSON.stringify([1, 2, 3]), 'group1 Group has correct initial value'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].output)).to.eq(JSON.stringify([]), 'group1 Group has correct initial output'); expect(JSON.stringify(MY_COLLECTION.groups['group1'].states)).to.eq(JSON.stringify([]), 'group1 Group has correct initial states'); diff --git a/packages/core/tests/collection/selector/default.spec.ts b/packages/core/tests/collection/selector/default.spec.ts index 61683127..500d9df4 100644 --- a/packages/core/tests/collection/selector/default.spec.ts +++ b/packages/core/tests/collection/selector/default.spec.ts @@ -31,7 +31,7 @@ describe('Default Selector Tests', () => { it('Has correct initial values', () => { expect(MY_COLLECTION.selectors['selector1'] instanceof Selector).to.eq(true, 'MY_COLLECTION selector1 Selector has been created'); - expect(MY_COLLECTION.selectors['selector1']?.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION selector1 Selector has correct subs size'); + expect(MY_COLLECTION.selectors['selector1']?.observer.dep.subs.size === 1).to.eq(true, 'MY_COLLECTION selector1 Selector has correct subs size'); expect(MY_COLLECTION.selectors['selector1'].key).to.eq('selector1', 'selector1 Selector has correct initial key'); expect(MY_COLLECTION.selectors['selector1'].id).to.eq(1, 'selector1 Selector has correct initial id'); expect(MY_COLLECTION.selectors['selector1'].exists).to.eq(false, 'selector1 Selector doesn\'t exist'); diff --git a/packages/core/tests/computed/default.spec.ts b/packages/core/tests/computed/default.spec.ts index b6ad74a5..84370142 100644 --- a/packages/core/tests/computed/default.spec.ts +++ b/packages/core/tests/computed/default.spec.ts @@ -47,7 +47,8 @@ describe('Default Computed Tests', () => { expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye_jeff_hans', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_2, MY_SELECTOR, MY_COLLECTION.findById(2)]), 'MY_COMPUTED has correct deps'); + // @ts-ignore + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_2.observer, MY_SELECTOR.observer, MY_COLLECTION.findById(2).observer]), 'MY_COMPUTED has correct deps'); expect(computedCallCount).to.eq(2, 'computedCallCount has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); }); @@ -120,8 +121,9 @@ describe('Default Computed Tests', () => { expect(MY_COMPUTED instanceof Computed).to.eq(true, 'MY_COMPUTED is computed'); expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye_jeff_hans', 'MY_COMPUTED has correct value'); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_3]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3, MY_STATE, MY_STATE_2, MY_SELECTOR, MY_COLLECTION.findById(2)]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_3.observer]), 'MY_COMPUTED has correct hardCodedDeps'); + // @ts-ignore + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3.observer, MY_STATE.observer, MY_STATE_2.observer, MY_SELECTOR.observer, MY_COLLECTION.findById(2).observer]), 'MY_COMPUTED has correct deps'); expect(computedCallCount).to.eq(2, 'computedCallCount has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); }); diff --git a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts b/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts index e047bcb2..92d58812 100644 --- a/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts +++ b/packages/core/tests/computed/functions/updateComputeFunction.function.spec.ts @@ -30,7 +30,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.key).to.eq(undefined, 'MY_COMPUTED has correct initial key'); expect(MY_COMPUTED.value).to.eq('hello_bye', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_2]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_2.observer]), 'MY_COMPUTED has correct deps'); expect(myComputed).to.eq('hello_bye', 'myComputed has correct initial value'); expect(rerenderCount).to.eq(0, 'rerenderCount has correct initial value'); @@ -47,7 +47,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('jeff_hans', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3, MY_STATE_4]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_3.observer, MY_STATE_4.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(1, 'rerenderCount has been increased by 1'); }); @@ -62,8 +62,8 @@ describe('updateComputeFunction Function test_integration', () => { await new Promise(resolve => setTimeout(resolve, 100)); expect(MY_COMPUTED.value).to.eq('bye_jeff', 'MY_COMPUTED has correct value'); - expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_4]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4, MY_STATE_2, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([MY_STATE_4.observer]), 'MY_COMPUTED has correct hardCodedDeps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4.observer, MY_STATE_2.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(2, 'rerenderCount has been increased by 1'); }); @@ -79,7 +79,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('bye_jeff', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_2, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_2.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(2, 'rerenderCount stayed the same'); }); @@ -96,7 +96,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('hello_jeff', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE, MY_STATE_3]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE.observer, MY_STATE_3.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(3, 'rerenderCount has been increased by 1'); }); @@ -112,7 +112,7 @@ describe('updateComputeFunction Function test_integration', () => { expect(MY_COMPUTED.value).to.eq('hans_bye', 'MY_COMPUTED has correct value'); expect(JSON.stringify(MY_COMPUTED.hardCodedDeps)).to.eq(JSON.stringify([]), 'MY_COMPUTED has correct hardCodedDeps'); - expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4, MY_STATE_2]), 'MY_COMPUTED has correct deps'); + expect(JSON.stringify(MY_COMPUTED.deps)).to.eq(JSON.stringify([MY_STATE_4.observer, MY_STATE_2.observer]), 'MY_COMPUTED has correct deps'); expect(rerenderCount).to.eq(3, 'rerenderCount stayed the same'); }); diff --git a/packages/core/tests/state/default.spec.ts b/packages/core/tests/state/default.spec.ts index eac9f38e..51596d39 100644 --- a/packages/core/tests/state/default.spec.ts +++ b/packages/core/tests/state/default.spec.ts @@ -22,7 +22,7 @@ describe('Default State Tests', () => { expect(MY_STATE.initialState).to.eq('hello', 'MY_STATE has correct initialState'); expect(MY_STATE.exists).to.eq(true, 'MY_STATE has correct exists'); expect(MY_STATE.isSet).to.eq(false, 'MY_STATE has correct isSet'); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); expect(MY_STATE.isPlaceholder).to.eq(false, 'MY_STATE has correct isPlaceholder'); expect(MY_STATE.valueType).to.eq(undefined, 'MY_STATE has correct valueType'); expect(MY_STATE.exists).to.eq(true, 'MY_STATE exists'); diff --git a/packages/core/tests/state/functions/patch.function.spec.ts b/packages/core/tests/state/functions/patch.function.spec.ts index feaeb06b..6787520f 100644 --- a/packages/core/tests/state/functions/patch.function.spec.ts +++ b/packages/core/tests/state/functions/patch.function.spec.ts @@ -34,7 +34,7 @@ describe('Patch Function Tests', () => { id: 1, name: 'jeff' }), 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(JSON.stringify(myHookState)).to.eq(JSON.stringify({ diff --git a/packages/core/tests/state/functions/persist.function.spec.ts b/packages/core/tests/state/functions/persist.function.spec.ts index a0c892be..288c4054 100644 --- a/packages/core/tests/state/functions/persist.function.spec.ts +++ b/packages/core/tests/state/functions/persist.function.spec.ts @@ -30,7 +30,7 @@ describe('Persist Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersistState'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); }); @@ -39,8 +39,8 @@ describe('Persist Function Tests', () => { // Persist State MY_STATE.persist(); - expect(MY_STATE.persistSettings.isPersisted).to.eq(false, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq(undefined, 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(false, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq(undefined, 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq(undefined, 'MY_STATE has correct key'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(false, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(undefined, 'MY_STATE isn\'t in storage'); @@ -53,8 +53,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq('mySecondKey', 'MY_STATE key has been set to persistKey if no key is provided'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE isn\'t in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(1, 'MY_STATE is in storage'); @@ -65,8 +65,8 @@ describe('Persist Function Tests', () => { // Reset State MY_STATE.reset(); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE has correct persistKey'); expect(MY_STATE.key).to.eq('mySecondKey', 'MY_STATE has correct key'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE is in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(undefined, 'MY_STATE isn\'t in storage'); @@ -78,8 +78,8 @@ describe('Persist Function Tests', () => { // Reset State MY_STATE.set(5); - expect(MY_STATE.persistSettings.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); - expect(MY_STATE.persistSettings.persistKey).to.eq('mySecondKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE.persistConfig.isPersisted).to.eq(true, 'MY_STATE has correct isPersisted'); + expect(MY_STATE.persistConfig.persistKey).to.eq('mySecondKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(App.storage.persistedStates.has(MY_STATE)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('mySecondKey')).to.eq(5, 'MY_STATE_WITH_KEY is in storage and has been updated'); }); @@ -93,7 +93,7 @@ describe('Persist Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE_WITH_KEY.value).to.eq('hello', 'MY_STATE_WITH_KEY has correct value'); expect(MY_STATE_WITH_KEY.key).to.eq('myKey', 'MY_STATE_WITH_KEY has correct key'); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(false, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(false, 'MY_STATE_WITH_KEY has correct isPersistState'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(false, 'MY_STATE_WITH_KEY isn\'t in persistedStates'); expect(App.storage.get('myKey')).to.eq(undefined, 'MY_STATE_WITH_KEY isn\'t in storage'); }); @@ -105,8 +105,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); - expect(MY_STATE_WITH_KEY.persistSettings.persistKey).to.eq('myKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.persistKey).to.eq('myKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('myKey')).to.eq('hello', 'MY_STATE_WITH_KEY is in storage'); }); @@ -118,8 +118,8 @@ describe('Persist Function Tests', () => { // Needs some time to persist value await new Promise(resolve => setTimeout(resolve, 100)); - expect(MY_STATE_WITH_KEY.persistSettings.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); - expect(MY_STATE_WITH_KEY.persistSettings.persistKey).to.eq('myThirdKey', 'MY_STATE_WITH_KEY has correct persistKey'); + expect(MY_STATE_WITH_KEY.persistConfig.isPersisted).to.eq(true, 'MY_STATE_WITH_KEY has correct isPersistState'); + expect(MY_STATE_WITH_KEY.persistConfig.persistKey).to.eq('myThirdKey', 'MY_STATE_WITH_KEY has correct persistKey'); expect(MY_STATE_WITH_KEY.key).to.eq('myKey', 'MY_STATE_WITH_KEY has correct key'); expect(App.storage.persistedStates.has(MY_STATE_WITH_KEY)).to.eq(true, 'MY_STATE_WITH_KEY is in persistedStates'); expect(App.storage.get('myThirdKey')).to.eq('hello', 'MY_STATE_WITH_KEY with new key is in storage'); diff --git a/packages/core/tests/state/functions/reset.function.spec.ts b/packages/core/tests/state/functions/reset.function.spec.ts index d12edd91..34b3956d 100644 --- a/packages/core/tests/state/functions/reset.function.spec.ts +++ b/packages/core/tests/state/functions/reset.function.spec.ts @@ -25,7 +25,7 @@ describe('Reset Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/core/tests/state/functions/set.function.spec.ts b/packages/core/tests/state/functions/set.function.spec.ts index 1c3e4b93..10df7d45 100644 --- a/packages/core/tests/state/functions/set.function.spec.ts +++ b/packages/core/tests/state/functions/set.function.spec.ts @@ -25,7 +25,7 @@ describe('Set Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/core/tests/state/functions/undo.function.spec.ts b/packages/core/tests/state/functions/undo.function.spec.ts index ecd73978..2627e887 100644 --- a/packages/core/tests/state/functions/undo.function.spec.ts +++ b/packages/core/tests/state/functions/undo.function.spec.ts @@ -25,7 +25,7 @@ describe('Undo Function Tests', () => { it('Has correct initial values', () => { expect(MY_STATE.value).to.eq(1, 'MY_STATE has correct value'); - expect(MY_STATE.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); + expect(MY_STATE.observer.dep.subs.size === 1).to.eq(true, 'MY_STATE has correct subs size (Subs are components/callbackFunctions which causes rerender)'); expect(typeof MY_STATE.sideEffects === 'function').to.eq(true, 'MY_STATE has sideEffect function'); expect(myHookState).to.eq(1, 'myHookState has correct MY_STATE value'); diff --git a/packages/core/tests/test_integration/hooks/useAgile_Test.ts b/packages/core/tests/test_integration/hooks/useAgile_Test.ts index 458716e5..0e1a321f 100644 --- a/packages/core/tests/test_integration/hooks/useAgile_Test.ts +++ b/packages/core/tests/test_integration/hooks/useAgile_Test.ts @@ -58,7 +58,7 @@ export function useAgile_Test, Y } // Create a callback base subscription, Callback invokes re-render Trigger - agileInstance?.subController.subscribeWithSubsArray(callbackFunction, depsArray); + agileInstance?.subController.subscribeWithSubsArray(callbackFunction, depsArray.map(state => state.observer)); return getReturnValue(depsArray); } diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index 0d03e49d..61cce528 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -1,6 +1,6 @@ { "name": "@agile-ts/react", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,22 @@ "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.51", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.51.tgz", + "integrity": "sha512-lQa12IyO+DMlnSZ3+AGHRUiUcpK47aakMMoBG8f7HGxJT8Yfe+WE128HIXaHOHVPReAW0oDS3KAI0JI2DDe1PQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -36,6 +52,12 @@ "which": "^2.0.1" } }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==", + "dev": true + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -90,6 +112,21 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "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" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -108,6 +145,12 @@ "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", "dev": true }, + "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 + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -129,6 +172,17 @@ "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "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" + } + }, "ps-tree": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", @@ -138,6 +192,23 @@ "event-stream": "=3.3.4" } }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/packages/react/package.json b/packages/react/package.json index 71342e94..1b643a84 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,7 +23,9 @@ "ts-node": "^8.10.2", "tsc-watch": "^4.1.0", "tslib": "^2.0.0", - "typescript": "^3.9.7" + "typescript": "^3.9.7", + "react": "^16.13.1", + "@types/react": "^16.9.51" }, "publishConfig": { "access": "public" diff --git a/packages/react/src/hooks/AgileHOC.ts b/packages/react/src/hooks/AgileHOC.ts index 5bfbded6..c86452f7 100644 --- a/packages/react/src/hooks/AgileHOC.ts +++ b/packages/react/src/hooks/AgileHOC.ts @@ -1,13 +1,13 @@ import React from "react"; -import {State, Agile, ComponentSubscriptionContainer, getAgileInstance, normalizeArray} from "@agile-ts/core"; +import {State, Agile, ComponentSubscriptionContainer, getAgileInstance, normalizeArray, Observer} from "@agile-ts/core"; export function AgileHOC(ReactComponent: any, deps?: Array | { [key: string]: State } | State, agileInstance?: Agile) { - let depsArray: Array; - let depsObject: { [key: string]: State }; + let depsArray: Array; + let depsObject: { [key: string]: Observer } = {}; if (deps instanceof State || Array.isArray(deps)) { // Normalize Dependencies - depsArray = normalizeArray(deps || []); + depsArray = normalizeArray(deps || []).map(dep => dep.observer); // Get Agile Instance if (!agileInstance) { @@ -19,7 +19,9 @@ export function AgileHOC(ReactComponent: any, deps?: Array | { [key: stri } } } else if (typeof deps === "object") { - depsObject = deps; + for (let dep in deps) { + depsObject[dep] = deps[dep].observer; + } // Get Agile Instance if (!agileInstance) { diff --git a/packages/react/src/hooks/useAgile.ts b/packages/react/src/hooks/useAgile.ts index 8cc80a26..1a820b56 100644 --- a/packages/react/src/hooks/useAgile.ts +++ b/packages/react/src/hooks/useAgile.ts @@ -61,7 +61,7 @@ export function useAgile, Y exte () => { set_({}); }, - depsArray + depsArray.map(dep => dep.observer) ); // Unsubscribe on Unmount