jac-client 0.2.2__py3-none-any.whl → 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/examples/all-in-one/src/app.jac +841 -0
  3. jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
  4. jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
  5. jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
  6. jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
  7. jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +7 -7
  8. jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
  9. jac_client/examples/basic-auth/src/app.jac +377 -0
  10. jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
  11. jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
  12. jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
  13. jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
  14. jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
  15. jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
  16. jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
  17. jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
  18. jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
  19. jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
  20. jac_client/examples/little-x/src/submit-button.jac +16 -0
  21. jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
  22. jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
  23. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
  24. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
  25. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
  26. jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
  27. jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
  28. jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
  29. jac_client/examples/ts-support/src/app.jac +35 -0
  30. jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
  31. jac_client/plugin/cli.jac +547 -0
  32. jac_client/plugin/client.jac +52 -0
  33. jac_client/plugin/client_runtime.cl.jac +38 -0
  34. jac_client/plugin/impl/client.impl.jac +134 -0
  35. jac_client/plugin/impl/client_runtime.impl.jac +177 -0
  36. jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
  37. jac_client/plugin/plugin_config.jac +195 -0
  38. jac_client/plugin/src/__init__.jac +20 -0
  39. jac_client/plugin/src/asset_processor.jac +33 -0
  40. jac_client/plugin/src/babel_processor.jac +18 -0
  41. jac_client/plugin/src/compiler.jac +66 -0
  42. jac_client/plugin/src/config_loader.jac +32 -0
  43. jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
  44. jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
  45. jac_client/plugin/src/impl/compiler.impl.jac +251 -0
  46. jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
  47. jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
  48. jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
  49. jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
  50. jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
  51. jac_client/plugin/src/import_processor.jac +19 -0
  52. jac_client/plugin/src/jac_to_js.jac +35 -0
  53. jac_client/plugin/src/package_installer.jac +26 -0
  54. jac_client/plugin/src/vite_bundler.jac +36 -0
  55. jac_client/plugin/vite_client_bundle.jac +31 -0
  56. jac_client/tests/conftest.py +281 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +2 -2
  58. jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
  59. jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
  60. jac_client/tests/fixtures/js_import/app.jac +5 -5
  61. jac_client/tests/fixtures/spawn_test/app.jac +7 -7
  62. jac_client/tests/fixtures/with-ts/app.jac +35 -0
  63. jac_client/tests/test_cli.py +755 -0
  64. jac_client/tests/test_it.py +347 -67
  65. {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/METADATA +30 -24
  66. jac_client-0.2.6.dist-info/RECORD +74 -0
  67. {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/WHEEL +2 -1
  68. jac_client-0.2.6.dist-info/entry_points.txt +4 -0
  69. jac_client-0.2.6.dist-info/top_level.txt +1 -0
  70. jac_client/docs/README.md +0 -689
  71. jac_client/docs/advanced-state.md +0 -1265
  72. jac_client/docs/asset-serving/intro.md +0 -209
  73. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  74. jac_client/docs/assets/pipe_line.png +0 -0
  75. jac_client/docs/file-system/app.jac.md +0 -121
  76. jac_client/docs/file-system/backend-frontend.md +0 -217
  77. jac_client/docs/file-system/intro.md +0 -72
  78. jac_client/docs/file-system/nested-imports.md +0 -348
  79. jac_client/docs/guide-example/intro.md +0 -115
  80. jac_client/docs/guide-example/step-01-setup.md +0 -270
  81. jac_client/docs/guide-example/step-02-components.md +0 -416
  82. jac_client/docs/guide-example/step-03-styling.md +0 -478
  83. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  84. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  85. jac_client/docs/guide-example/step-06-events.md +0 -749
  86. jac_client/docs/guide-example/step-07-effects.md +0 -468
  87. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  88. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  89. jac_client/docs/guide-example/step-10-routing.md +0 -539
  90. jac_client/docs/guide-example/step-11-final.md +0 -963
  91. jac_client/docs/imports.md +0 -1141
  92. jac_client/docs/lifecycle-hooks.md +0 -773
  93. jac_client/docs/routing.md +0 -659
  94. jac_client/docs/styling/intro.md +0 -249
  95. jac_client/docs/styling/js-styling.md +0 -367
  96. jac_client/docs/styling/material-ui.md +0 -341
  97. jac_client/docs/styling/pure-css.md +0 -299
  98. jac_client/docs/styling/sass.md +0 -403
  99. jac_client/docs/styling/styled-components.md +0 -395
  100. jac_client/docs/styling/tailwind.md +0 -298
  101. jac_client/examples/all-in-one/.babelrc +0 -9
  102. jac_client/examples/all-in-one/README.md +0 -16
  103. jac_client/examples/all-in-one/app.jac +0 -426
  104. jac_client/examples/all-in-one/assets/burger.png +0 -0
  105. jac_client/examples/all-in-one/package.json +0 -29
  106. jac_client/examples/all-in-one/styles.css +0 -26
  107. jac_client/examples/all-in-one/vite.config.js +0 -28
  108. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  109. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  110. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  111. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  112. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  113. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  114. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  115. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  116. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  117. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  118. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  119. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  120. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  121. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  122. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  123. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  124. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  125. jac_client/examples/basic/.babelrc +0 -9
  126. jac_client/examples/basic/README.md +0 -16
  127. jac_client/examples/basic/package.json +0 -27
  128. jac_client/examples/basic/vite.config.js +0 -27
  129. jac_client/examples/basic-auth/.babelrc +0 -9
  130. jac_client/examples/basic-auth/README.md +0 -16
  131. jac_client/examples/basic-auth/app.jac +0 -308
  132. jac_client/examples/basic-auth/package.json +0 -27
  133. jac_client/examples/basic-auth/vite.config.js +0 -27
  134. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  135. jac_client/examples/basic-auth-with-router/README.md +0 -60
  136. jac_client/examples/basic-auth-with-router/package.json +0 -28
  137. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  138. jac_client/examples/basic-full-stack/.babelrc +0 -9
  139. jac_client/examples/basic-full-stack/README.md +0 -18
  140. jac_client/examples/basic-full-stack/package.json +0 -28
  141. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  142. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  143. jac_client/examples/css-styling/js-styling/README.md +0 -183
  144. jac_client/examples/css-styling/js-styling/package.json +0 -28
  145. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  146. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  147. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  148. jac_client/examples/css-styling/material-ui/README.md +0 -16
  149. jac_client/examples/css-styling/material-ui/package.json +0 -32
  150. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  151. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  152. jac_client/examples/css-styling/pure-css/README.md +0 -16
  153. jac_client/examples/css-styling/pure-css/package.json +0 -28
  154. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  155. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  156. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  157. jac_client/examples/css-styling/sass-example/README.md +0 -16
  158. jac_client/examples/css-styling/sass-example/package.json +0 -29
  159. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  160. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  161. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  162. jac_client/examples/css-styling/styled-components/README.md +0 -16
  163. jac_client/examples/css-styling/styled-components/package.json +0 -29
  164. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  165. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  166. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  167. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  168. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  169. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  170. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  171. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  172. jac_client/examples/full-stack-with-auth/README.md +0 -16
  173. jac_client/examples/full-stack-with-auth/package.json +0 -28
  174. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  175. jac_client/examples/little-x/package.json +0 -23
  176. jac_client/examples/little-x/submit-button.jac +0 -8
  177. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  178. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  179. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  180. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  181. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  182. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  183. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  184. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  185. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  186. jac_client/examples/with-router/.babelrc +0 -9
  187. jac_client/examples/with-router/README.md +0 -17
  188. jac_client/examples/with-router/package.json +0 -28
  189. jac_client/examples/with-router/vite.config.js +0 -27
  190. jac_client/plugin/cli.py +0 -244
  191. jac_client/plugin/client.py +0 -152
  192. jac_client/plugin/client_runtime.jac +0 -234
  193. jac_client/plugin/vite_client_bundle.py +0 -503
  194. jac_client/tests/fixtures/js_import/utils.js +0 -21
  195. jac_client/tests/fixtures/package-lock.json +0 -329
  196. jac_client/tests/fixtures/package.json +0 -11
  197. jac_client/tests/test_asset_examples.py +0 -322
  198. jac_client/tests/test_cl.py +0 -530
  199. jac_client/tests/test_create_jac_app.py +0 -131
  200. jac_client/tests/test_nested_file.py +0 -374
  201. jac_client-0.2.2.dist-info/RECORD +0 -171
  202. jac_client-0.2.2.dist-info/entry_points.txt +0 -4
@@ -1,773 +0,0 @@
1
- # Lifecycle Hooks in Jac: Component Lifecycle Management
2
-
3
- Learn how to use React's `useEffect` and `useState` hooks to manage component state, initialization, side effects, and cleanup.
4
-
5
- ---
6
-
7
- ## Table of Contents
8
-
9
- - [What are Lifecycle Hooks?](#what-are-lifecycle-hooks)
10
- - [React Hooks (Recommended)](#react-hooks-recommended)
11
- - [useState](#usestate)
12
- - [useEffect](#useeffect)
13
- - [Common Use Cases](#common-use-cases)
14
- - [Complete Examples](#complete-examples)
15
- - [Best Practices](#best-practices)
16
- - [Legacy Jac Hooks](#legacy-jac-hooks)
17
-
18
- ---
19
-
20
- ## What are Lifecycle Hooks?
21
-
22
- Lifecycle hooks are functions that let you run code at specific points in a component's lifecycle:
23
- - **When component mounts**: Run initialization code once
24
- - **When component updates**: React to state changes
25
- - **When component unmounts**: Clean up resources
26
-
27
- **Jac uses React hooks as the standard approach:**
28
- - **useState**: Manage component state
29
- - **useEffect**: Handle side effects, lifecycle events, and cleanup
30
-
31
- **Key Benefits:**
32
- - **Initialization**: Load data when component appears
33
- - **Side Effects**: Set up subscriptions, timers, or listeners
34
- - **Reactive Updates**: Run code when specific dependencies change
35
- - **Cleanup**: Properly clean up resources when components unmount
36
- - **Standard React API**: Works exactly like React hooks you already know
37
-
38
- ---
39
-
40
- ## React Hooks (Recommended)
41
-
42
- ### useState
43
-
44
- The `useState` hook lets you add state to your components.
45
-
46
- #### Basic Usage
47
-
48
- ```jac
49
- cl import from react { useState }
50
-
51
- cl {
52
- def Counter() -> any {
53
- let [count, setCount] = useState(0);
54
-
55
- return <div>
56
- <h1>Count: {count}</h1>
57
- <button onClick={lambda e: any -> None {
58
- setCount(count + 1);
59
- }}>
60
- Increment
61
- </button>
62
- </div>;
63
- }
64
- }
65
- ```
66
-
67
- **Key Points:**
68
- - Import `useState` from `react`
69
- - Returns an array: `[currentValue, setterFunction]`
70
- - Use destructuring to get the value and setter
71
- - Call the setter function to update state
72
-
73
- #### Multiple State Variables
74
-
75
- ```jac
76
- cl import from react { useState }
77
-
78
- cl {
79
- def TodoApp() -> any {
80
- let [todos, setTodos] = useState([]);
81
- let [inputValue, setInputValue] = useState("");
82
- let [filter, setFilter] = useState("all");
83
-
84
- return <div>Todo App</div>;
85
- }
86
- }
87
- ```
88
-
89
- ### useEffect
90
-
91
- The `useEffect` hook lets you perform side effects in your components. It provides full lifecycle management including mount, update, and cleanup.
92
-
93
- #### Basic Usage - Run on Mount
94
-
95
- ```jac
96
- cl import from react { useState, useEffect }
97
-
98
- cl {
99
- def MyComponent() -> any {
100
- let [data, setData] = useState(None);
101
-
102
- useEffect(lambda -> None {
103
- console.log("Component mounted!");
104
- # Load initial data
105
- async def loadData() -> None {
106
- result = await jacSpawn("get_data", "", {});
107
- setData(result);
108
- }
109
- loadData();
110
- }, []); # Empty array means run only on mount
111
-
112
- return <div>My Component</div>;
113
- }
114
- }
115
- ```
116
-
117
- **Key Points:**
118
- - Import `useEffect` from `react`
119
- - First argument: function to run
120
- - Second argument: dependency array
121
- - `[]` - run only on mount
122
- - `[count]` - run when `count` changes
123
- - No array - run on every render
124
-
125
- #### useEffect with Dependencies
126
-
127
- ```jac
128
- cl import from react { useState, useEffect }
129
-
130
- cl {
131
- def Counter() -> any {
132
- let [count, setCount] = useState(0);
133
-
134
- useEffect(lambda -> None {
135
- console.log("Count changed to:", count);
136
- document.title = "Count: " + str(count);
137
- }, [count]); # Run when count changes
138
-
139
- return <div>
140
- <h1>Count: {count}</h1>
141
- <button onClick={lambda e: any -> None {
142
- setCount(count + 1);
143
- }}>
144
- Increment
145
- </button>
146
- </div>;
147
- }
148
- }
149
- ```
150
-
151
- #### useEffect with Cleanup
152
-
153
- ```jac
154
- cl import from react { useEffect }
155
-
156
- cl {
157
- def TimerComponent() -> any {
158
- useEffect(lambda -> any {
159
- # Setup
160
- intervalId = setInterval(lambda -> None {
161
- console.log("Timer tick");
162
- }, 1000);
163
-
164
- # Cleanup function (returned from useEffect)
165
- return lambda -> None {
166
- clearInterval(intervalId);
167
- };
168
- }, []);
169
-
170
- return <div>Timer Component</div>;
171
- }
172
- }
173
- ```
174
-
175
- ---
176
-
177
- ## Common Use Cases
178
-
179
- ### 1. Loading Initial Data
180
-
181
- The most common use case is loading data when a component mounts:
182
-
183
- ```jac
184
- cl import from react { useState, useEffect }
185
- cl import from '@jac-client/utils' { jacSpawn }
186
-
187
- cl {
188
- def TodoApp() -> any {
189
- let [todos, setTodos] = useState([]);
190
- let [loading, setLoading] = useState(True);
191
-
192
- useEffect(lambda -> None {
193
- async def loadTodos() -> None {
194
- setLoading(True);
195
-
196
- # Fetch todos from backend
197
- result = await jacSpawn("read_todos", "", {});
198
- console.log(result);
199
- setTodos(result.reports);
200
- setLoading(False);
201
- }
202
- loadTodos();
203
- }, []); # Empty array = run only on mount
204
-
205
- if loading {
206
- return <div>Loading...</div>;
207
- }
208
-
209
- return <div>
210
- {todos.map(lambda todo: any -> any {
211
- return <TodoItem todo={todo} />;
212
- })}
213
- </div>;
214
- }
215
- }
216
- ```
217
-
218
- ### 2. Setting Up Event Listeners
219
-
220
- Set up event listeners with proper cleanup:
221
-
222
- ```jac
223
- cl import from react { useState, useEffect }
224
-
225
- cl {
226
- def WindowResizeHandler() -> any {
227
- let [width, setWidth] = useState(0);
228
- let [height, setHeight] = useState(0);
229
-
230
- useEffect(lambda -> any {
231
- def handleResize() -> None {
232
- setWidth(window.innerWidth);
233
- setHeight(window.innerHeight);
234
- }
235
-
236
- # Set initial size
237
- handleResize();
238
-
239
- # Add listener
240
- window.addEventListener("resize", handleResize);
241
-
242
- # Cleanup function
243
- return lambda -> None {
244
- window.removeEventListener("resize", handleResize);
245
- };
246
- }, []);
247
-
248
- return <div>
249
- Window size: {width} x {height}
250
- </div>;
251
- }
252
- }
253
- ```
254
-
255
- ### 3. Fetching User Data
256
-
257
- Load user-specific data when a component mounts:
258
-
259
- ```jac
260
- cl import from react { useState, useEffect }
261
- cl import from '@jac-client/utils' { jacSpawn }
262
-
263
- cl {
264
- def ProfileView() -> any {
265
- let [profile, setProfile] = useState(None);
266
- let [loading, setLoading] = useState(True);
267
-
268
- useEffect(lambda -> None {
269
- async def loadUserProfile() -> None {
270
- if not jacIsLoggedIn() {
271
- navigate("/login");
272
- return;
273
- }
274
-
275
- # Fetch user profile
276
- result = await jacSpawn("get_user_profile", "", {});
277
- setProfile(result);
278
- setLoading(False);
279
- }
280
- loadUserProfile();
281
- }, []);
282
-
283
- if loading {
284
- return <div>Loading profile...</div>;
285
- }
286
-
287
- if not profile {
288
- return <div>No profile found</div>;
289
- }
290
-
291
- return <div>
292
- <h1>{profile.username}</h1>
293
- <p>{profile.email}</p>
294
- </div>;
295
- }
296
- }
297
- ```
298
-
299
- ### 4. Initializing Third-Party Libraries
300
-
301
- Initialize external libraries or APIs:
302
-
303
- ```jac
304
- cl import from react { useEffect }
305
-
306
- cl {
307
- def ChartComponent() -> any {
308
- useEffect(lambda -> any {
309
- # Initialize chart library
310
- chart = new Chart("myChart", {
311
- "type": "line",
312
- "data": chartData,
313
- "options": chartOptions
314
- });
315
-
316
- # Cleanup function
317
- return lambda -> None {
318
- chart.destroy();
319
- };
320
- }, []);
321
-
322
- return <canvas id="myChart"></canvas>;
323
- }
324
- }
325
- ```
326
-
327
- ### 5. Focusing Input Fields
328
-
329
- Focus an input field when a component mounts:
330
-
331
- ```jac
332
- cl import from react { useEffect }
333
-
334
- cl {
335
- def SearchBar() -> any {
336
- useEffect(lambda -> None {
337
- # Focus search input on mount
338
- inputEl = document.getElementById("search-input");
339
- if inputEl {
340
- inputEl.focus();
341
- }
342
- }, []);
343
-
344
- return <input
345
- id="search-input"
346
- type="text"
347
- placeholder="Search..."
348
- />;
349
- }
350
- }
351
- ```
352
-
353
- ---
354
-
355
- ## Complete Examples
356
-
357
- ### Example 1: Todo App with Data Loading
358
-
359
- ```jac
360
- cl import from react { useState, useEffect }
361
- cl import from '@jac-client/utils' { jacSpawn }
362
-
363
- cl {
364
- def app() -> any {
365
- let [todos, setTodos] = useState([]);
366
- let [inputValue, setInputValue] = useState("");
367
- let [filter, setFilter] = useState("all");
368
-
369
- useEffect(lambda -> None {
370
- async def loadTodos() -> None {
371
- todos = await jacSpawn("read_todos","",{});
372
- console.log(todos);
373
- setTodos(todos.reports);
374
- }
375
- loadTodos();
376
- }, []);
377
-
378
- # Add a new todo
379
- async def addTodo() -> None {
380
- if not inputValue.trim() { return; }
381
- newTodo = {
382
- "id": Date.now(),
383
- "text": inputValue.trim(),
384
- "done": False
385
- };
386
- await jacSpawn("create_todo","", {"text": inputValue.trim()});
387
- newTodos = todos.concat([newTodo]);
388
- setTodos(newTodos);
389
- setInputValue("");
390
- }
391
-
392
- # Toggle todo completion status
393
- async def toggleTodo(id: any) -> None {
394
- await jacSpawn("toggle_todo",id, {});
395
- setTodos(todos.map(lambda todo: any -> any {
396
- if todo._jac_id == id {
397
- updatedTodo = {
398
- "_jac_id": todo._jac_id,
399
- "text": todo.text,
400
- "done": not todo.done,
401
- "id": todo.id
402
- };
403
- return updatedTodo;
404
- }
405
- return todo;
406
- }));
407
- }
408
-
409
- # Filter todos based on current filter
410
- def getFilteredTodos() -> list {
411
- if filter == "active" {
412
- return todos.filter(lambda todo: any -> bool { return not todo.done; });
413
- } elif filter == "completed" {
414
- return todos.filter(lambda todo: any -> bool { return todo.done; });
415
- }
416
- return todos;
417
- }
418
-
419
- filteredTodos = getFilteredTodos();
420
-
421
- return <div style={{
422
- "maxWidth": "600px",
423
- "margin": "40px auto",
424
- "padding": "24px",
425
- "fontFamily": "system-ui, -apple-system, sans-serif"
426
- }}>
427
- <h1 style={{"textAlign": "center"}}> My Todo App</h1>
428
-
429
- # Add todo form
430
- <div style={{"display": "flex", "gap": "8px", "marginBottom": "24px"}}>
431
- <input
432
- type="text"
433
- value={inputValue}
434
- onChange={lambda e: any -> None { setInputValue(e.target.value); }}
435
- onKeyPress={lambda e: any -> None {
436
- if e.key == "Enter" { addTodo(); }
437
- }}
438
- placeholder="What needs to be done?"
439
- style={{"flex": "1", "padding": "12px"}}
440
- />
441
- <button onClick={addTodo} style={{"padding": "12px 24px"}}>
442
- Add
443
- </button>
444
- </div>
445
-
446
- # Filter buttons
447
- <div style={{"display": "flex", "gap": "8px", "marginBottom": "16px"}}>
448
- <button onClick={lambda -> None { setFilter("all"); }}>All</button>
449
- <button onClick={lambda -> None { setFilter("active"); }}>Active</button>
450
- <button onClick={lambda -> None { setFilter("completed"); }}>Completed</button>
451
- </div>
452
-
453
- # Todo list
454
- <ul>
455
- {filteredTodos.map(lambda todo: any -> any {
456
- return <li key={todo._jac_id}>
457
- <input
458
- type="checkbox"
459
- checked={todo.done}
460
- onChange={lambda -> None { toggleTodo(todo._jac_id); }}
461
- />
462
- <span>{todo.text}</span>
463
- </li>;
464
- })}
465
- </ul>
466
- </div>;
467
- }
468
- }
469
- ```
470
-
471
- ### Example 2: Dashboard with Multiple Data Sources
472
-
473
- ```jac
474
- cl import from react { useState, useEffect }
475
- cl import from '@jac-client/utils' { jacSpawn }
476
-
477
- cl {
478
- def Dashboard() -> any {
479
- let [stats, setStats] = useState(None);
480
- let [activity, setActivity] = useState([]);
481
- let [loading, setLoading] = useState(True);
482
-
483
- useEffect(lambda -> None {
484
- async def loadDashboardData() -> None {
485
- setLoading(True);
486
-
487
- # Load multiple data sources in parallel
488
- results = await Promise.all([
489
- jacSpawn("get_stats", "", {}),
490
- jacSpawn("get_recent_activity", "", {})
491
- ]);
492
-
493
- setStats(results[0]);
494
- setActivity(results[1].reports);
495
- setLoading(False);
496
- }
497
- loadDashboardData();
498
- }, []);
499
-
500
- if loading {
501
- return <div>Loading dashboard...</div>;
502
- }
503
-
504
- return <div>
505
- <StatsView stats={stats} />
506
- <ActivityList activities={activity} />
507
- </div>;
508
- }
509
- }
510
- ```
511
-
512
- ### Example 3: Timer Component with Cleanup
513
-
514
- Proper cleanup when component unmounts:
515
-
516
- ```jac
517
- cl import from react { useState, useEffect }
518
-
519
- cl {
520
- def TimerComponent() -> any {
521
- let [seconds, setSeconds] = useState(0);
522
-
523
- useEffect(lambda -> any {
524
- # Set up timer
525
- intervalId = setInterval(lambda -> None {
526
- setSeconds(lambda prev: int -> int { return prev + 1; });
527
- }, 1000);
528
-
529
- # Cleanup function - runs when component unmounts
530
- return lambda -> None {
531
- clearInterval(intervalId);
532
- };
533
- }, []);
534
-
535
- return <div>Timer: {seconds} seconds</div>;
536
- }
537
- }
538
- ```
539
-
540
- ---
541
-
542
- ## Best Practices
543
-
544
- ### 1. Always Specify Dependencies
545
-
546
- Be explicit about what your effect depends on:
547
-
548
- ```jac
549
- # Good: Empty array for mount-only effects
550
- useEffect(lambda -> None {
551
- loadInitialData();
552
- }, []);
553
-
554
- # Good: Specify dependencies
555
- useEffect(lambda -> None {
556
- console.log("Count changed:", count);
557
- }, [count]);
558
-
559
- # Warning: No dependency array runs on every render
560
- useEffect(lambda -> None {
561
- console.log("Runs on every render!");
562
- });
563
- ```
564
-
565
- ### 2. Handle Async Operations Properly
566
-
567
- Always handle async operations with proper error handling:
568
-
569
- ```jac
570
- # Good: Proper async handling
571
- useEffect(lambda -> None {
572
- async def loadData() -> None {
573
- try {
574
- data = await jacSpawn("get_data", "", {});
575
- setData(data);
576
- } except Exception as err {
577
- console.error("Error loading data:", err);
578
- setError(err);
579
- }
580
- }
581
- loadData();
582
- }, []);
583
- ```
584
-
585
- ### 3. Clean Up Side Effects
586
-
587
- Always clean up event listeners, timers, and subscriptions:
588
-
589
- ```jac
590
- # Good: Cleanup function removes event listener
591
- useEffect(lambda -> any {
592
- def handleResize() -> None {
593
- setWidth(window.innerWidth);
594
- }
595
-
596
- window.addEventListener("resize", handleResize);
597
-
598
- return lambda -> None {
599
- window.removeEventListener("resize", handleResize);
600
- };
601
- }, []);
602
- ```
603
-
604
- ### 4. Use Loading States
605
-
606
- Show loading indicators while data is being fetched:
607
-
608
- ```jac
609
- # Good: Clear loading states
610
- def Component() -> any {
611
- let [data, setData] = useState(None);
612
- let [loading, setLoading] = useState(True);
613
- let [error, setError] = useState(None);
614
-
615
- useEffect(lambda -> None {
616
- async def loadData() -> None {
617
- try {
618
- setLoading(True);
619
- result = await jacSpawn("get_data", "", {});
620
- setData(result);
621
- } except Exception as err {
622
- setError(err);
623
- } finally {
624
- setLoading(False);
625
- }
626
- }
627
- loadData();
628
- }, []);
629
-
630
- if loading { return <div>Loading...</div>; }
631
- if error { return <div>Error: {error}</div>; }
632
- return <div>{data}</div>;
633
- }
634
- ```
635
-
636
- ### 5. Keep Effects Focused
637
-
638
- Each effect should have a single responsibility:
639
-
640
- ```jac
641
- # Good: Separate effects for separate concerns
642
- def Component() -> any {
643
- useEffect(lambda -> None {
644
- loadData(); # Data loading
645
- }, []);
646
-
647
- useEffect(lambda -> any {
648
- # Event listener setup
649
- window.addEventListener("resize", handleResize);
650
- return lambda -> None {
651
- window.removeEventListener("resize", handleResize);
652
- };
653
- }, []);
654
-
655
- return <div>Component</div>;
656
- }
657
- ```
658
-
659
- ### 6. Avoid Stale Closures
660
-
661
- Be careful with closures capturing old state values:
662
-
663
- ```jac
664
- # Avoid: Stale closure problem
665
- useEffect(lambda -> None {
666
- setInterval(lambda -> None {
667
- setCount(count + 1); # count is stale!
668
- }, 1000);
669
- }, []);
670
-
671
- # Good: Use functional update
672
- useEffect(lambda -> any {
673
- intervalId = setInterval(lambda -> None {
674
- setCount(lambda prev: int -> int { return prev + 1; });
675
- }, 1000);
676
-
677
- return lambda -> None {
678
- clearInterval(intervalId);
679
- };
680
- }, []);
681
- ```
682
-
683
- ---
684
-
685
- ## Summary
686
-
687
- - **useState**: Manage component state (replaces `createState()`, `createSignal()`)
688
- - **useEffect**: Handle side effects and lifecycle events (replaces `onMount()`, `createEffect()`)
689
- - **Dependencies**: Always specify what your effect depends on
690
- - **Cleanup**: Return a cleanup function for subscriptions, timers, and listeners
691
- - **Best Practices**: Handle errors, use loading states, keep effects focused
692
-
693
- React hooks provide a powerful and standard way to manage component lifecycle!
694
-
695
- ---
696
-
697
- ## Legacy Jac Hooks
698
-
699
- > **Note**: The following hooks are from older Jac versions. New projects should use React hooks instead.
700
-
701
- ### `onMount()` - Legacy
702
-
703
- The `onMount()` hook was a Jac-specific hook for running code once when a component mounts:
704
-
705
- ```jac
706
- # Legacy approach - use useEffect instead
707
- def Component() -> any {
708
- onMount(lambda -> None {
709
- loadData();
710
- });
711
- return <div>Component</div>;
712
- }
713
- ```
714
-
715
- **Modern equivalent:**
716
-
717
- ```jac
718
- # Modern approach with React hooks
719
- def Component() -> any {
720
- useEffect(lambda -> None {
721
- loadData();
722
- }, []);
723
- return <div>Component</div>;
724
- }
725
- ```
726
-
727
- ### `createState()` - Legacy
728
-
729
- The `createState()` hook was a Jac-specific state management solution:
730
-
731
- ```jac
732
- # Legacy approach - use useState instead
733
- let [state, setState] = createState({"count": 0});
734
-
735
- def Component() -> any {
736
- s = state();
737
- return <div>{s.count}</div>;
738
- }
739
- ```
740
-
741
- **Modern equivalent:**
742
-
743
- ```jac
744
- # Modern approach with React hooks
745
- def Component() -> any {
746
- let [count, setCount] = useState(0);
747
- return <div>{count}</div>;
748
- }
749
- ```
750
-
751
- ### `createSignal()` and `createEffect()` - Legacy
752
-
753
- These were Signal-based reactive primitives from Jac:
754
-
755
- ```jac
756
- # Legacy approach
757
- let [count, setCount] = createSignal(0);
758
-
759
- createEffect(lambda -> None {
760
- console.log("Count:", count());
761
- });
762
- ```
763
-
764
- **Modern equivalent:**
765
-
766
- ```jac
767
- # Modern approach with React hooks
768
- let [count, setCount] = useState(0);
769
-
770
- useEffect(lambda -> None {
771
- console.log("Count:", count);
772
- }, [count]);
773
- ```