1- import React from 'react' ;
2- import Agile , {
3- getAgileInstance ,
1+ import {
42 Observer ,
53 State ,
6- SubscriptionContainerKeyType ,
7- isValidObject ,
84 generateId ,
9- ProxyWeakMapType ,
10- ComponentIdType ,
115 extractRelevantObservers ,
12- SelectorWeakMapType ,
13- SelectorMethodType ,
14- LogCodeManager ,
156 normalizeArray ,
167 defineConfig ,
178} from '@agile-ts/core' ;
189import type { Collection , Group } from '@agile-ts/core' ; // Only import Collection and Group type for better Treeshaking
19- import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect' ;
20-
21- // TODO https://stackoverflow.com/questions/68148235/require-module-inside-a-function-doesnt-work
22- let proxyPackage : any = null ;
23- try {
24- proxyPackage = require ( '@agile-ts/proxytree' ) ;
25- } catch ( e ) {
26- // empty catch block
27- }
10+ import {
11+ BaseAgileHookConfigInterface ,
12+ getReturnValue ,
13+ SubscribableAgileInstancesType ,
14+ useBaseAgile ,
15+ } from './useBaseAgile' ;
2816
2917/**
3018 * A React Hook for binding the most relevant value of multiple Agile Instances
@@ -67,162 +55,38 @@ export function useAgile<
6755) : AgileOutputHookArrayType < X > | AgileOutputHookType < Y > {
6856 config = defineConfig ( config , {
6957 key : generateId ( ) ,
70- proxyBased : false ,
7158 agileInstance : null as any ,
7259 componentId : undefined ,
7360 observerType : undefined ,
7461 deps : [ ] ,
62+ handleReturn : ( dep : Observer | undefined ) => {
63+ return dep != null ? dep . value : undefined ;
64+ } ,
7565 } ) ;
7666 const depsArray = extractRelevantObservers (
7767 normalizeArray ( deps ) ,
7868 config . observerType
7969 ) ;
80- const proxyTreeWeakMap = new WeakMap ( ) ;
81-
82- // Builds return value,
83- // depending on whether the deps were provided in array shape or not
84- const getReturnValue = (
85- depsArray : ( Observer | undefined ) [ ]
86- ) : AgileOutputHookArrayType < X > | AgileOutputHookType < Y > => {
87- const handleReturn = (
88- dep : Observer | undefined
89- ) : AgileOutputHookType < Y > => {
90- if ( dep == null ) return undefined as any ;
91- const value = dep . value ;
92-
93- // If proxyBased and the value is of the type object.
94- // Wrap a Proxy around the object to track the accessed properties.
95- if ( config . proxyBased && isValidObject ( value , true ) ) {
96- if ( proxyPackage != null ) {
97- const { ProxyTree } = proxyPackage ;
98- const proxyTree = new ProxyTree ( value ) ;
99- proxyTreeWeakMap . set ( dep , proxyTree ) ;
100- return proxyTree . proxy ;
101- } else {
102- console . error (
103- 'In order to use the Agile proxy functionality, ' +
104- `the installation of an additional package called '@agile-ts/proxytree' is required!`
105- ) ;
106- }
107- }
108-
109- // If specified selector function and the value is of type object.
110- // Return the selected value.
111- // (Destroys the type of the useAgile hook,
112- // however the type can be adjusted in the useSelector hook)
113- if ( config . selector && isValidObject ( value , true ) ) {
114- return config . selector ( value ) ;
115- }
116-
117- return value ;
118- } ;
119-
120- // Handle single dep return value
121- if ( depsArray . length === 1 && ! Array . isArray ( deps ) ) {
122- return handleReturn ( depsArray [ 0 ] ) ;
123- }
124-
125- // Handle deps array return value
126- return depsArray . map ( ( dep ) => {
127- return handleReturn ( dep ) ;
128- } ) as AgileOutputHookArrayType < X > ;
129- } ;
130-
131- // Trigger State, used to force Component to rerender
132- const [ , forceRender ] = React . useReducer ( ( s ) => s + 1 , 0 ) ;
133-
134- useIsomorphicLayoutEffect ( ( ) => {
135- let agileInstance = config . agileInstance ;
136-
137- // https://github.com/microsoft/TypeScript/issues/20812
138- const observers : Observer [ ] = depsArray . filter (
139- ( dep ) : dep is Observer => dep !== undefined
140- ) ;
141-
142- // Try to extract Agile Instance from the specified Instance/s
143- if ( ! agileInstance ) agileInstance = getAgileInstance ( observers [ 0 ] ) ;
144- if ( ! agileInstance || ! agileInstance . subController ) {
145- LogCodeManager . getLogger ( ) ?. error (
146- 'Failed to subscribe Component with deps because of missing valid Agile Instance.' ,
147- deps
148- ) ;
149- return ;
150- }
151-
152- // TODO Proxy doesn't work as expected when 'selecting' a not yet existing property.
153- // For example you select the 'user.data.name' property, but the 'user' object is undefined.
154- // -> No correct Proxy Path could be created on the Component mount, since the to select property doesn't exist
155- // -> Selector was created based on the not complete Proxy Path
156- // -> Component re-renders to often
157- //
158- // Build Proxy Path WeakMap based on the Proxy Tree WeakMap
159- // by extracting the routes from the Proxy Tree.
160- // Building the Path WeakMap in the 'useIsomorphicLayoutEffect'
161- // because the 'useIsomorphicLayoutEffect' is called after the rerender.
162- // -> All used paths in the UI-Component were successfully tracked.
163- let proxyWeakMap : ProxyWeakMapType | undefined = undefined ;
164- if ( config . proxyBased && proxyPackage != null ) {
165- proxyWeakMap = new WeakMap ( ) ;
166- for ( const observer of observers ) {
167- const proxyTree = proxyTreeWeakMap . get ( observer ) ;
168- if ( proxyTree != null ) {
169- proxyWeakMap . set ( observer , {
170- paths : proxyTree . getUsedRoutes ( ) as any ,
171- } ) ;
172- }
173- }
174- }
175-
176- // Build Selector WeakMap based on the specified selector method
177- let selectorWeakMap : SelectorWeakMapType | undefined = undefined ;
178- if ( config . selector != null ) {
179- selectorWeakMap = new WeakMap ( ) ;
180- for ( const observer of observers ) {
181- selectorWeakMap . set ( observer , { methods : [ config . selector ] } ) ;
182- }
183- }
184-
185- // Create Callback based Subscription
186- const subscriptionContainer = agileInstance . subController . subscribe (
187- ( ) => {
188- forceRender ( ) ;
189- } ,
190- observers ,
191- {
192- key : config . key ,
193- proxyWeakMap,
194- waitForMount : false ,
195- componentId : config . componentId ,
196- selectorWeakMap,
197- }
198- ) ;
19970
200- // Unsubscribe Callback based Subscription on unmount
201- return ( ) => {
202- agileInstance ?. subController . unsubscribe ( subscriptionContainer ) ;
203- } ;
204- } , config . deps ) ;
71+ useBaseAgile (
72+ depsArray ,
73+ ( ) => ( {
74+ key : config . key ,
75+ waitForMount : false ,
76+ componentId : config . componentId ,
77+ } ) ,
78+ config . deps || [ ] ,
79+ config . agileInstance
80+ ) ;
20581
206- return getReturnValue ( depsArray ) ;
82+ return getReturnValue (
83+ depsArray ,
84+ config . handleReturn as any ,
85+ Array . isArray ( deps )
86+ ) ;
20787}
20888
209- export type SubscribableAgileInstancesType =
210- | State
211- | Collection < any , any > //https://stackoverflow.com/questions/66987727/type-classa-id-number-name-string-is-not-assignable-to-type-classar
212- | Observer
213- | undefined ;
214-
215- export interface AgileHookConfigInterface {
216- /**
217- * Key/Name identifier of the Subscription Container to be created.
218- * @default undefined
219- */
220- key ?: SubscriptionContainerKeyType ;
221- /**
222- * Instance of Agile the Subscription Container belongs to.
223- * @default `undefined` if no Agile Instance could be extracted from the provided Instances.
224- */
225- agileInstance ?: Agile ;
89+ export interface AgileHookConfigInterface extends BaseAgileHookConfigInterface {
22690 /**
22791 * Whether to wrap a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
22892 * around the bound Agile Instance value object,
@@ -234,7 +98,7 @@ export interface AgileHookConfigInterface {
23498 *
23599 * @default false
236100 */
237- proxyBased ?: boolean ;
101+ // proxyBased?: boolean;
238102 /**
239103 * Equality comparison function
240104 * that allows you to customize the way the selected Agile Instance
@@ -245,12 +109,8 @@ export interface AgileHookConfigInterface {
245109 *
246110 * @default undefined
247111 */
248- selector ?: SelectorMethodType ;
249- /**
250- * Key/Name identifier of the UI-Component the Subscription Container is bound to.
251- * @default undefined
252- */
253- componentId ?: ComponentIdType ;
112+ // selector?: SelectorMethodType;
113+
254114 /**
255115 * What type of Observer to be bound to the UI-Component.
256116 *
@@ -261,14 +121,9 @@ export interface AgileHookConfigInterface {
261121 */
262122 observerType ?: string ;
263123 /**
264- * Dependencies that determine, in addition to unmounting and remounting the React-Component,
265- * when the specified Agile Sub Instances should be re-subscribed to the React-Component.
266- *
267- * [Github issue](https://github.com/agile-ts/agile/issues/170)
268- *
269- * @default []
124+ * TODO
270125 */
271- deps ?: any [ ] ;
126+ handleReturn ?: ( dep : Observer | undefined ) => any ;
272127}
273128
274129// Array Type
0 commit comments