@@ -5,31 +5,37 @@ sidebar_label: Introduction
5
5
slug : /core/integration
6
6
---
7
7
8
- The ` Integration Class ` is used to integrate AgileTs into other frameworks like [ React] ( https://reactjs.org/ ) .
8
+ ::: info
9
+
10
+ The ` Integration Class ` is useful for creating an Integration for a specific Framework.
11
+ Luckily there already exists some Integrations, so we don't have to built them from scratch.
12
+ Check [ here] ( ../../../../Frameworks.md ) if your required Integration already exists.
13
+
14
+ :::
15
+
16
+ The ` Integration Class ` is an Interface for AgileTs to other Frameworks like [ React] ( https://reactjs.org/ ) .
17
+ It is mainly used to cause rerender on Components which has subscribed a particular State for reactivity.
9
18
``` ts
10
19
const reactIntegration = new Integration <typeof React , AgileReactComponent >({
11
20
key: ' myFramework' ,
12
21
frameworkInstance: MyFramework ,
13
22
bind() {
14
- // Will be called on the instantiaten of AgileTs
23
+ // Will be called as soon as the Integration gets integrated into AgileTs
15
24
},
16
- updateMethod(componentInstance , updatedData : Object ) {
17
- // Will be called on each Agile Sub Instance mutation
25
+ updateMethod(componentInstance , updatedData ) {
26
+ // Will be called on each Agile Sub Instance mutation (Component based Subscription)
18
27
// For instance if MY_STATE value mutates from 'jeff' to 'hans'
19
- // componentInstance: The Component to which the State got subscribed (In React it might be the Class Component)
20
- // updatedData: The data which got updated, for instance {myState: 'hans'}
28
+ // Function based Subscriptions use a callback to cause a rerender on Components and therefore don't call 'updateMethod()'
29
+ // componentInstance: The Component to which the State got subscribed to (In React it might be the Class Component)
30
+ // updatedData: The data which got updated, for instance '{myState: 'hans'}'
21
31
}
22
-
23
32
});
24
33
```
25
34
35
+ ## 💾 Example
26
36
27
-
28
-
29
-
30
-
31
-
32
-
37
+ ### 🔵 [ ` React ` ] ( https://reactjs.org/ )
38
+ Here is how the React Integration looks like.
33
39
``` ts
34
40
import { Agile , flatMerge , Integration } from ' @agile-ts/core' ;
35
41
import { AgileReactComponent } from ' ./hocs/AgileHOC' ;
@@ -42,6 +48,8 @@ const reactIntegration = new Integration<typeof React, AgileReactComponent>({
42
48
// Nothing to bind ;D
43
49
return Promise .resolve (true );
44
50
},
51
+ // Used to update State in Class Component (Component based Subscription)
52
+ // (Function Components use a Function based Subscription therefore they don't call 'updateMethod()')
45
53
updateMethod(componentInstance , updatedData : Object ) {
46
54
// Merge changes into State if some Data updated otherwise force rerender
47
55
if (Object .keys (updatedData ).length !== 0 ) {
@@ -61,6 +69,215 @@ Agile.initialIntegrations.push(reactIntegration);
61
69
62
70
export default reactIntegration ;
63
71
```
72
+ However, to properly use AgileTs in React Function and Class Components there were some other things to do.
73
+ For instance, we had to create the ` useAgile ` Hook or the ` AgileHOC ` to properly subscribe States to Components.
74
+ So lets take a quick look how a simplified ` useAgile ` Hook looks like.
75
+ ``` ts
76
+ import React from ' react' ;
77
+ import {
78
+ Agile ,
79
+ getAgileInstance ,
80
+ extractObservers ,
81
+ Observer ,
82
+ State ,
83
+ } from ' @agile-ts/core' ;
84
+
85
+ function useAgile(deps : Array <State | Collection | Observer | undefined >, agileInstance ? : Agile ){
86
+ // Extract Observers from passed Agile Sub Instances
87
+ // Each State has an Observer, which can be subscribed to a Component
88
+ // Through such Observer the State is able to trigger rerenders on the subscribed Components
89
+ const depsArray = extractObservers (deps );
90
+
91
+ // Trigger State, used to force Component to rerender
92
+ const [, forceRender] = React .useReducer ((s ) => s + 1 , 0 );
93
+
94
+ useEffect (() => {
95
+ // Try to get Agile Instance from passed Instances, otherwise drop error
96
+ if (! agileInstance ) agileInstance = getAgileInstance (depsArray [0 ]);
97
+ if (! agileInstance || ! agileInstance .subController ) {
98
+ Agile .logger .error (' Failed to subscribe Component with deps' , depsArray );
99
+ return ;
100
+ }
101
+
102
+ // Remove undefined Observers, since we can't subscirbe a not existing Observer
103
+ const observers: Observer [] = depsArray .filter (
104
+ (dep ): dep is Observer => dep !== undefined
105
+ );
106
+
107
+ // Create Callback based Subscription
108
+ // -> whenever a subscribed State mutates, this callback will be called
109
+ // or if its an Component based Subscription the 'updateMethod()' in the React Integration (used in AgileHOC)
110
+ const subscriptionContainer = agileInstance .subController .subscribeWithSubsArray (
111
+ () => {
112
+ forceRender ();
113
+ },
114
+ observers ,
115
+ key
116
+ );
117
+
118
+ // Unsubscribe Callback based Subscription on Unmount (cleaning up, otherwise it can lead to memory leaks)
119
+ return () => {
120
+ agileInstance ?.subController .unsubscribe (subscriptionContainer );
121
+ };
122
+ }, []);
123
+
124
+ // Create return value
125
+ return depsArray .map ((dep ) => {
126
+ return dep ?.value ;
127
+ });
128
+ }
129
+ ```
130
+
131
+
132
+ ## Subscriptions
133
+
134
+ To properly create an Integration for AgileTs,
135
+ we need a basic understanding of how States can be bound/subscribed to Components.
136
+ Such subscriptions are important to cause rerender on the Component as soon as the State mutates.
137
+ In AgileTs there are two ways of binding a State to a Component:
138
+
139
+ ### ` Component ` based
140
+
141
+ A ` Component based Subscription ` is thought for Components which handle their States in a specific property.
142
+ For instance in React Class Components the ` this.state ` property.
143
+ If a State mutates, we simply can merge the changed State values into the Component State property.
144
+ This will trigger a rerender.
145
+ ``` ts
146
+ const MY_STATE = App .createState (' hans' , {key: ' myState' });
147
+ this .agileInstance ().subController .subscribeWithSubsArray (
148
+ MyComponent ,
149
+ [MY_STATE .observer ]
150
+ );
151
+ ```
152
+ Now if the State mutates
153
+ ``` ts
154
+ MY_STATE .set (' jeff' );
155
+ ```
156
+ the ` updateMethod() ` defined in the Integration will be called
157
+ ``` ts
158
+ // ..
159
+ updateMethod (componentInstance , updatedData ){
160
+ console .log (componentInstance ); // Returns 'MyComponent'
161
+ console .log (updatedData ); // Returns '{myState: 'jeff'}'
162
+ }
163
+ // ..
164
+ ```
165
+
166
+ ### ` Function ` based
167
+
168
+ A ` Function based Subscription ` is thought for Components which don't have a specific property to handle States.
169
+ So we can't trigger a rerender by mutating the State property of a Component.
170
+ Therefore, we came across another solution. A callback function which triggers a rerender on the Component.
171
+ This callback function will be called instead of the ` updateMethd() ` , whenever a bound State mutates.
172
+ ``` ts
173
+ const MY_STATE = App .createState (' hans' , {key: ' myState' });
174
+ this .agileInstance ().subController .subscribeWithSubsArray (
175
+ () => {console .log (' Called callback' )},
176
+ [MY_STATE .observer ]
177
+ );
178
+ ```
179
+ Now if the State mutates
180
+ ``` ts
181
+ MY_STATE .set (' jeff' );
182
+ ```
183
+ the callback function will be called.
184
+ ``` ts
185
+ // console: 'Called callback'
186
+ ```
187
+
188
+
189
+ ## 📭 Props
190
+
191
+ ``` ts
192
+ new Integration (config );
193
+ ```
194
+
195
+ ### ` config `
196
+
197
+ A ` Integration ` takes a required configuration object as its only parameter.
198
+ Here is a Typescript Interface of the configuration object for quick reference,
199
+ however each property will be explained in more detail below.
200
+ ``` ts
201
+ export interface CreateIntegrationConfig <F = any , C = any >
202
+ extends IntegrationMethods <C > {
203
+ key: string ;
204
+ frameworkInstance? : F ;
205
+ }
206
+
207
+ // or without extending
208
+
209
+ export interface CreateIntegrationConfig <F = any , C = any > {
210
+ key: string ;
211
+ frameworkInstance? : F ;
212
+ bind? : (agileInstance : Agile ) => Promise <boolean >;
213
+ updateMethod? : (componentInstance : C , updatedData : Object ) => void ;
214
+ }
215
+ ```
216
+
217
+ <br />
218
+
219
+ #### ` key `
220
+
221
+ The property ` key/name ` should be a unique ` string/number ` to identify the Integration later.
222
+ ``` ts
223
+ new Integration ({
224
+ key: " myIntegration"
225
+ // ..
226
+ });
227
+ ```
228
+
229
+ | Type | Default | Required |
230
+ | --------------------| -------------| ----------|
231
+ | ` string \| number ` | undefined | Yes |
232
+
233
+ <br />
234
+
235
+ #### ` frameworkInstance `
236
+
237
+ The Instance of the Framework the Integration represents.
238
+ For instance in case of [ React] ( https://reactjs.org ) it should be the 'React' Instance.
239
+
240
+ | Type | Default | Required |
241
+ | --------------------| -------------| ----------|
242
+ | ` any ` | undefined | No |
243
+
244
+ <br />
245
+
246
+ #### ` frameworkInstance `
247
+
248
+ The Instance of the Framework the Integration represents.
249
+ For instance in case of [ React] ( https://reactjs.org ) it should be the ` React ` Instance.
250
+
251
+ | Type | Default | Required |
252
+ | --------------------| -------------| ----------|
253
+ | ` any ` | undefined | No |
254
+
255
+ <br />
256
+
257
+ #### ` bind `
258
+
259
+ Will be called as soon as the Integration gets integrated into AgileTs.
260
+ It might be used to configure something's in the Framework the Integration represents.
261
+ If that's done it should return ` true ` if the Framework is ready and ` false ` if it's not.
262
+
263
+ | Type | Default | Required |
264
+ | ----------------------------------------------------| -------------| ----------|
265
+ | ` (agileInstance: Agile) => Promise<boolean> ` | undefined | No |
266
+
267
+ <br />
268
+
269
+ #### ` updateMethod `
270
+
271
+ Will be called as soon as a to a Component (` componentInstance ` ) subscribed State mutates.
272
+ Be aware that this is only the case if it is a ` Component based Subscription ` ,
273
+ since in ` Function based Subscription ` a callback function will be called to trigger a rerender on the Component.
274
+
275
+ | Type | Default | Required |
276
+ | --------------------------------------------------------| -------------| ----------|
277
+ | ` (componentInstance: C, updatedData: Object) => void ` | undefined | No |
278
+
279
+
280
+
64
281
65
282
66
283
0 commit comments