jac-client 0.2.3__py3-none-any.whl → 0.2.5__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} +3 -3
  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.3.dist-info → jac_client-0.2.5.dist-info}/METADATA +28 -30
  66. jac_client-0.2.5.dist-info/RECORD +74 -0
  67. {jac_client-0.2.3.dist-info → jac_client-0.2.5.dist-info}/WHEEL +2 -1
  68. jac_client-0.2.5.dist-info/entry_points.txt +4 -0
  69. jac_client-0.2.5.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.3.dist-info/RECORD +0 -171
  202. jac_client-0.2.3.dist-info/entry_points.txt +0 -4
@@ -1,468 +0,0 @@
1
- # Step 7: Component Lifecycle with `useEffect`
2
-
3
- > ** Quick Tip:** Each step has two parts. **Part 1** shows you what to build. **Part 2** explains why it works. Want to just build? Skip all Part 2 sections!
4
-
5
- In this step, you'll learn about **useEffect** - a way to run code when your component loads or when data changes!
6
-
7
- ---
8
-
9
- ## Part 1: Building the App
10
-
11
- ### Step 7.1: Understanding the Problem
12
-
13
- Right now, your todos reset every time you refresh the page. We need to:
14
- 1. Load todos when the app starts
15
- 2. Save todos when they change
16
-
17
- We'll use `useEffect` to handle this!
18
-
19
- ### Step 7.2: Add useEffect Import
20
-
21
- First, import `useEffect`:
22
-
23
- ```jac
24
- cl import from react {useState, useEffect}
25
-
26
- cl {
27
- # ... your components
28
- }
29
- ```
30
-
31
- ### Step 7.3: Run Code When App Loads
32
-
33
- Let's log a message when the app starts:
34
-
35
- ```jac
36
- cl import from react {useState, useEffect}
37
-
38
- cl {
39
- # ... (keep all your components from step 6)
40
-
41
- def app() -> any {
42
- let [todos, setTodos] = useState([]);
43
- let [input, setInput] = useState("");
44
- let [filter, setFilter] = useState("all");
45
-
46
- # Run once when component mounts
47
- useEffect(lambda -> None {
48
- console.log("App loaded!");
49
- }, []);
50
-
51
- # ... rest of your code
52
- }
53
- }
54
- ```
55
-
56
- **Open browser console (F12) and refresh** - you'll see "App loaded!" printed once!
57
-
58
- ### Step 7.4: Save to localStorage
59
-
60
- Let's persist todos using localStorage:
61
-
62
- ```jac
63
- cl import from react {useState, useEffect}
64
-
65
- cl {
66
- # ... (keep all components)
67
-
68
- def app() -> any {
69
- let [todos, setTodos] = useState([]);
70
- let [input, setInput] = useState("");
71
- let [filter, setFilter] = useState("all");
72
-
73
- # Load todos from localStorage when app mounts
74
- useEffect(lambda -> None {
75
- let saved = localStorage.getItem("todos");
76
- if saved {
77
- let parsed = JSON.parse(saved);
78
- setTodos(parsed);
79
- }
80
- }, []);
81
-
82
- # Save todos to localStorage whenever they change
83
- useEffect(lambda -> None {
84
- localStorage.setItem("todos", JSON.stringify(todos));
85
- }, [todos]);
86
-
87
- # ... (keep all your functions: addTodo, toggleTodo, deleteTodo, getFilteredTodos)
88
-
89
- # ... (keep your return statement with all the UI)
90
- }
91
- }
92
- ```
93
-
94
- **Try it!** Add some todos, then refresh the page - your todos persist!
95
-
96
- ### Step 7.5: Add Loading State
97
-
98
- Let's add a loading indicator:
99
-
100
- ```jac
101
- cl import from react {useState, useEffect}
102
-
103
- cl {
104
- # ... (keep all components)
105
-
106
- def app() -> any {
107
- let [todos, setTodos] = useState([]);
108
- let [input, setInput] = useState("");
109
- let [filter, setFilter] = useState("all");
110
- let [loading, setLoading] = useState(true);
111
-
112
- # Load todos
113
- useEffect(lambda -> None {
114
- console.log("Loading todos...");
115
-
116
- # Simulate loading delay
117
- setTimeout(lambda -> None {
118
- let saved = localStorage.getItem("todos");
119
- if saved {
120
- let parsed = JSON.parse(saved);
121
- setTodos(parsed);
122
- }
123
- setLoading(false);
124
- }, 500);
125
- }, []);
126
-
127
- # Save todos whenever they change
128
- useEffect(lambda -> None {
129
- if not loading { # Don't save during initial load
130
- localStorage.setItem("todos", JSON.stringify(todos));
131
- }
132
- }, [todos]);
133
-
134
- # ... (keep all your functions)
135
-
136
- # Show loading state
137
- if loading {
138
- return <div style={{
139
- "display": "flex",
140
- "justifyContent": "center",
141
- "alignItems": "center",
142
- "height": "100vh"
143
- }}>
144
- <h2>Loading todos...</h2>
145
- </div>;
146
- }
147
-
148
- # ... (keep your normal UI return statement)
149
- }
150
- }
151
- ```
152
-
153
- **Try it!** You'll see a brief loading message before your todos appear!
154
-
155
- ---
156
-
157
- **⏭ Want to skip the theory?** Jump to [Step 8: Walkers](./step-08-walkers.md)
158
-
159
- ---
160
-
161
- ## Part 2: Understanding the Concepts
162
-
163
- ### What is `useEffect`?
164
-
165
- `useEffect` lets you run **side effects** - code that affects things outside your component.
166
-
167
- **Common side effects:**
168
- - Fetching data from a server
169
- - Saving data to localStorage
170
- - ⏰ Setting up timers
171
- - Logging analytics
172
- - Subscribing to events
173
-
174
- **Python analogy:**
175
-
176
- ```python
177
- # Python
178
- class TodoApp:
179
- def __init__(self):
180
- self.load_from_database() # Side effect: reads from DB
181
-
182
- # Jac
183
- def app() -> any {
184
- useEffect(lambda -> None {
185
- # Load data
186
- }, []);
187
- }
188
- ```
189
-
190
- ### useEffect Syntax
191
-
192
- ```jac
193
- useEffect(lambda -> None {
194
- # Code to run
195
- }, [dependencies]);
196
- ```
197
-
198
- **Two parameters:**
199
- 1. **Function** - What to run
200
- 2. **Dependencies** - When to re-run
201
-
202
- ### Dependency Array Controls When to Run
203
-
204
- **Run once (on mount):**
205
-
206
- ```jac
207
- useEffect(lambda -> None {
208
- console.log("Component mounted!");
209
- }, []); # Empty array = run once
210
- ```
211
-
212
- **Run when specific value changes:**
213
-
214
- ```jac
215
- useEffect(lambda -> None {
216
- console.log("Todos changed!");
217
- }, [todos]); # Run whenever todos changes
218
- ```
219
-
220
- **Run on every render (rarely used):**
221
-
222
- ```jac
223
- useEffect(lambda -> None {
224
- console.log("Component rendered!");
225
- }); # No array = run always (be careful!)
226
- ```
227
-
228
- ### Multiple useEffect Hooks
229
-
230
- You can use multiple `useEffect` hooks for different purposes:
231
-
232
- ```jac
233
- def app() -> any {
234
- let [todos, setTodos] = useState([]);
235
-
236
- # Effect 1: Load data once
237
- useEffect(lambda -> None {
238
- let saved = localStorage.getItem("todos");
239
- if saved {
240
- setTodos(JSON.parse(saved));
241
- }
242
- }, []);
243
-
244
- # Effect 2: Save when todos change
245
- useEffect(lambda -> None {
246
- localStorage.setItem("todos", JSON.stringify(todos));
247
- }, [todos]);
248
-
249
- # Effect 3: Log count changes
250
- useEffect(lambda -> None {
251
- console.log("Todo count:", todos.length);
252
- }, [todos.length]);
253
- }
254
- ```
255
-
256
- This keeps your code organized!
257
-
258
- ### useEffect Lifecycle
259
-
260
- ```
261
- 1. Component renders
262
-
263
- 2. UI updates on screen
264
-
265
- 3. useEffect runs
266
-
267
- 4. State changes (from effect)
268
-
269
- 5. Component re-renders
270
-
271
- 6. useEffect runs again (if dependencies changed)
272
- ```
273
-
274
- ### localStorage API
275
-
276
- Browser's built-in storage:
277
-
278
- ```jac
279
- # Save data
280
- localStorage.setItem("key", "value");
281
-
282
- # Load data
283
- let value = localStorage.getItem("key");
284
-
285
- # Remove data
286
- localStorage.removeItem("key");
287
-
288
- # Clear all
289
- localStorage.clear();
290
-
291
- # For objects/arrays, use JSON
292
- localStorage.setItem("todos", JSON.stringify(todos));
293
- let todos = JSON.parse(localStorage.getItem("todos"));
294
- ```
295
-
296
- **Storage limits:**
297
- - ~5-10 MB per domain
298
- - Persists across browser sessions
299
- - Only stores strings (use JSON for objects)
300
-
301
- ### Preventing Initial Save
302
-
303
- When loading from localStorage, you don't want to immediately save back:
304
-
305
- ```jac
306
- let [loading, setLoading] = useState(true);
307
-
308
- # Load
309
- useEffect(lambda -> None {
310
- # ... load data ...
311
- setLoading(false);
312
- }, []);
313
-
314
- # Save (skip during initial load)
315
- useEffect(lambda -> None {
316
- if not loading {
317
- localStorage.setItem("todos", JSON.stringify(todos));
318
- }
319
- }, [todos]);
320
- ```
321
-
322
- ### Common useEffect Patterns
323
-
324
- **Pattern 1: Fetch on mount**
325
-
326
- ```jac
327
- useEffect(lambda -> None {
328
- async def fetchData() -> None {
329
- let data = await apiCall();
330
- setState(data);
331
- }
332
- fetchData();
333
- }, []);
334
- ```
335
-
336
- **Pattern 2: Sync with external system**
337
-
338
- ```jac
339
- useEffect(lambda -> None {
340
- localStorage.setItem("key", value);
341
- }, [value]);
342
- ```
343
-
344
- **Pattern 3: Cleanup (timers, subscriptions)**
345
-
346
- ```jac
347
- useEffect(lambda -> None {
348
- let timerId = setInterval(lambda -> None {
349
- console.log("Tick");
350
- }, 1000);
351
-
352
- # Return cleanup function
353
- return lambda -> None {
354
- clearInterval(timerId);
355
- };
356
- }, []);
357
- ```
358
-
359
- **Pattern 4: Conditional effect**
360
-
361
- ```jac
362
- useEffect(lambda -> None {
363
- if someCondition {
364
- # Do something
365
- }
366
- }, [someCondition]);
367
- ```
368
-
369
- ---
370
-
371
- ## What You've Learned
372
-
373
- - What useEffect is and why we need it
374
- - How to run code when component mounts
375
- - Dependency arrays control when effects run
376
- - Multiple useEffect hooks for organization
377
- - Using localStorage to persist data
378
- - Adding loading states
379
- - Preventing unnecessary saves
380
-
381
- ---
382
-
383
- ## Common Issues
384
-
385
- ### Issue: Effect runs too many times
386
-
387
- **Check:** Is your dependency array correct?
388
-
389
- ```jac
390
- # Wrong - runs on every render
391
- useEffect(lambda -> None {
392
- console.log(todos);
393
- });
394
-
395
- # Correct - runs only when todos change
396
- useEffect(lambda -> None {
397
- console.log(todos);
398
- }, [todos]);
399
- ```
400
-
401
- ### Issue: Effect doesn't run when it should
402
-
403
- **Check:** Did you include all dependencies?
404
-
405
- ```jac
406
- # Wrong - missing todos dependency
407
- useEffect(lambda -> None {
408
- console.log(todos.length);
409
- }, []);
410
-
411
- # Correct - includes todos
412
- useEffect(lambda -> None {
413
- console.log(todos.length);
414
- }, [todos]);
415
- ```
416
-
417
- ### Issue: localStorage data not loading
418
-
419
- **Check:**
420
- - Are you parsing JSON? `JSON.parse(saved)`
421
- - Are you checking if data exists? `if saved { ... }`
422
- - Is the key name correct? `"todos"` in both save and load
423
-
424
- ### Issue: Infinite loop
425
-
426
- **Cause:** Effect updates state, which triggers effect again
427
-
428
- ```jac
429
- # Wrong - infinite loop!
430
- useEffect(lambda -> None {
431
- setTodos([...]); # This triggers effect again!
432
- }, [todos]);
433
-
434
- # Correct - run only once
435
- useEffect(lambda -> None {
436
- setTodos([...]);
437
- }, []);
438
- ```
439
-
440
- ---
441
-
442
- ## Quick Exercise
443
-
444
- Try adding a "last saved" timestamp:
445
-
446
- ```jac
447
- let [lastSaved, setLastSaved] = useState(None);
448
-
449
- useEffect(lambda -> None {
450
- if not loading and todos.length > 0 {
451
- localStorage.setItem("todos", JSON.stringify(todos));
452
- setLastSaved(Date().toLocaleTimeString());
453
- }
454
- }, [todos]);
455
-
456
- # Display it
457
- {(<p>Last saved: {lastSaved}</p>) if lastSaved else None}
458
- ```
459
-
460
- ---
461
-
462
- ## Next Step
463
-
464
- Great! Your app now persists data with localStorage. But localStorage is only local to your browser!
465
-
466
- In the next step, we'll add **real backend** using **walkers** so your data is stored on a server!
467
-
468
- **[Continue to Step 8: Walkers](./step-08-walkers.md)**