jac-client 0.2.0__py3-none-any.whl → 0.2.2__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.
- jac_client/docs/README.md +50 -20
- jac_client/docs/advanced-state.md +13 -14
- jac_client/docs/asset-serving/intro.md +209 -0
- jac_client/docs/assets/pipe_line-v2.svg +32 -0
- jac_client/docs/file-system/app.jac.md +121 -0
- jac_client/docs/file-system/backend-frontend.md +217 -0
- jac_client/docs/file-system/intro.md +72 -0
- jac_client/docs/file-system/nested-imports.md +348 -0
- jac_client/docs/guide-example/intro.md +11 -13
- jac_client/docs/guide-example/step-01-setup.md +30 -20
- jac_client/docs/guide-example/step-02-components.md +24 -24
- jac_client/docs/guide-example/step-03-styling.md +24 -24
- jac_client/docs/guide-example/step-04-todo-ui.md +17 -17
- jac_client/docs/guide-example/step-05-local-state.md +23 -23
- jac_client/docs/guide-example/step-06-events.md +23 -24
- jac_client/docs/guide-example/step-07-effects.md +27 -28
- jac_client/docs/guide-example/step-08-walkers.md +23 -23
- jac_client/docs/guide-example/step-09-authentication.md +18 -18
- jac_client/docs/guide-example/step-10-routing.md +20 -21
- jac_client/docs/guide-example/step-11-final.md +34 -35
- jac_client/docs/imports.md +4 -5
- jac_client/docs/lifecycle-hooks.md +12 -13
- jac_client/docs/routing.md +21 -22
- jac_client/docs/styling/intro.md +249 -0
- jac_client/docs/styling/js-styling.md +367 -0
- jac_client/docs/styling/material-ui.md +341 -0
- jac_client/docs/styling/pure-css.md +299 -0
- jac_client/docs/styling/sass.md +403 -0
- jac_client/docs/styling/styled-components.md +395 -0
- jac_client/docs/styling/tailwind.md +298 -0
- jac_client/examples/all-in-one/.babelrc +9 -0
- jac_client/examples/all-in-one/README.md +16 -0
- jac_client/examples/all-in-one/app.jac +426 -0
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/button.jac +7 -0
- jac_client/examples/all-in-one/components/button.jac +7 -0
- jac_client/examples/all-in-one/package.json +29 -0
- jac_client/examples/all-in-one/styles.css +26 -0
- jac_client/examples/all-in-one/vite.config.js +28 -0
- jac_client/examples/asset-serving/css-with-image/.babelrc +9 -0
- jac_client/examples/asset-serving/css-with-image/README.md +91 -0
- jac_client/examples/asset-serving/css-with-image/app.jac +88 -0
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +28 -0
- jac_client/examples/asset-serving/css-with-image/styles.css +26 -0
- jac_client/examples/asset-serving/css-with-image/vite.config.js +28 -0
- jac_client/examples/asset-serving/image-asset/.babelrc +9 -0
- jac_client/examples/asset-serving/image-asset/README.md +119 -0
- jac_client/examples/asset-serving/image-asset/app.jac +55 -0
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +28 -0
- jac_client/examples/asset-serving/image-asset/styles.css +26 -0
- jac_client/examples/asset-serving/image-asset/vite.config.js +28 -0
- jac_client/examples/asset-serving/import-alias/.babelrc +9 -0
- jac_client/examples/asset-serving/import-alias/README.md +83 -0
- jac_client/examples/asset-serving/import-alias/app.jac +111 -0
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +28 -0
- jac_client/examples/asset-serving/import-alias/vite.config.js +28 -0
- jac_client/examples/basic/app.jac +14 -9
- jac_client/examples/basic/package.json +1 -1
- jac_client/examples/basic/vite.config.js +0 -1
- jac_client/examples/basic-auth/package.json +1 -1
- jac_client/examples/basic-auth/vite.config.js +0 -1
- jac_client/examples/basic-auth-with-router/package.json +1 -1
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -1
- jac_client/examples/basic-full-stack/package.json +1 -1
- jac_client/examples/basic-full-stack/vite.config.js +0 -1
- jac_client/examples/css-styling/js-styling/.babelrc +9 -0
- jac_client/examples/css-styling/js-styling/README.md +183 -0
- jac_client/examples/css-styling/js-styling/app.jac +84 -0
- jac_client/examples/css-styling/js-styling/package.json +28 -0
- jac_client/examples/css-styling/js-styling/styles.js +100 -0
- jac_client/examples/css-styling/js-styling/vite.config.js +27 -0
- jac_client/examples/css-styling/material-ui/.babelrc +9 -0
- jac_client/examples/css-styling/material-ui/README.md +16 -0
- jac_client/examples/css-styling/material-ui/app.jac +122 -0
- jac_client/examples/css-styling/material-ui/package.json +32 -0
- jac_client/examples/css-styling/material-ui/vite.config.js +27 -0
- jac_client/examples/css-styling/pure-css/.babelrc +9 -0
- jac_client/examples/css-styling/pure-css/README.md +16 -0
- jac_client/examples/css-styling/pure-css/app.jac +64 -0
- jac_client/examples/css-styling/pure-css/package.json +28 -0
- jac_client/examples/css-styling/pure-css/styles.css +111 -0
- jac_client/examples/css-styling/pure-css/vite.config.js +27 -0
- jac_client/examples/css-styling/sass-example/.babelrc +9 -0
- jac_client/examples/css-styling/sass-example/README.md +16 -0
- jac_client/examples/css-styling/sass-example/app.jac +64 -0
- jac_client/examples/css-styling/sass-example/package.json +29 -0
- jac_client/examples/css-styling/sass-example/styles.scss +153 -0
- jac_client/examples/css-styling/sass-example/vite.config.js +27 -0
- jac_client/examples/css-styling/styled-components/.babelrc +9 -0
- jac_client/examples/css-styling/styled-components/README.md +16 -0
- jac_client/examples/css-styling/styled-components/app.jac +71 -0
- jac_client/examples/css-styling/styled-components/package.json +29 -0
- jac_client/examples/css-styling/styled-components/styled.js +90 -0
- jac_client/examples/css-styling/styled-components/vite.config.js +27 -0
- jac_client/examples/css-styling/tailwind-example/.babelrc +9 -0
- jac_client/examples/css-styling/tailwind-example/README.md +16 -0
- jac_client/examples/css-styling/tailwind-example/app.jac +63 -0
- jac_client/examples/css-styling/tailwind-example/global.css +1 -0
- jac_client/examples/css-styling/tailwind-example/package.json +30 -0
- jac_client/examples/css-styling/tailwind-example/vite.config.js +29 -0
- jac_client/examples/full-stack-with-auth/app.jac +20 -33
- jac_client/examples/full-stack-with-auth/package.json +1 -1
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -1
- jac_client/examples/little-x/app.jac +327 -218
- jac_client/examples/little-x/submit-button.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/.babelrc +9 -0
- jac_client/examples/nested-folders/nested-advance/ButtonRoot.jac +11 -0
- jac_client/examples/nested-folders/nested-advance/README.md +77 -0
- jac_client/examples/nested-folders/nested-advance/app.jac +35 -0
- jac_client/examples/nested-folders/nested-advance/level1/ButtonSecondL.jac +19 -0
- jac_client/examples/nested-folders/nested-advance/level1/Card.jac +43 -0
- jac_client/examples/nested-folders/nested-advance/level1/level2/ButtonThirdL.jac +25 -0
- jac_client/examples/nested-folders/nested-advance/package.json +29 -0
- jac_client/examples/nested-folders/nested-advance/vite.config.js +28 -0
- jac_client/examples/nested-folders/nested-basic/.babelrc +9 -0
- jac_client/examples/nested-folders/nested-basic/README.md +183 -0
- jac_client/examples/nested-folders/nested-basic/app.jac +13 -0
- jac_client/examples/nested-folders/nested-basic/app.js +7 -0
- jac_client/examples/nested-folders/nested-basic/button.jac +7 -0
- jac_client/examples/nested-folders/nested-basic/components/button.jac +7 -0
- jac_client/examples/nested-folders/nested-basic/package.json +28 -0
- jac_client/examples/nested-folders/nested-basic/vite.config.js +27 -0
- jac_client/examples/with-router/app.jac +1 -1
- jac_client/examples/with-router/package.json +1 -1
- jac_client/examples/with-router/vite.config.js +0 -1
- jac_client/plugin/cli.py +7 -2
- jac_client/plugin/client.py +68 -5
- jac_client/plugin/client_runtime.jac +1 -1
- jac_client/plugin/vite_client_bundle.py +162 -14
- jac_client/tests/__init__.py +0 -1
- jac_client/tests/fixtures/basic-app/app.jac +7 -2
- jac_client/tests/fixtures/cl_file/app.cl.jac +48 -0
- jac_client/tests/fixtures/cl_file/app.jac +15 -0
- jac_client/tests/fixtures/client_app_with_antd/app.jac +14 -8
- jac_client/tests/fixtures/js_import/app.jac +19 -15
- jac_client/tests/fixtures/js_import/utils.js +0 -1
- jac_client/tests/fixtures/package.json +1 -1
- jac_client/tests/fixtures/relative_import/app.jac +4 -6
- jac_client/tests/fixtures/relative_import/button.jac +7 -6
- jac_client/tests/fixtures/spawn_test/app.jac +1 -5
- jac_client/tests/fixtures/test_fragments_spread/app.jac +24 -10
- jac_client/tests/test_asset_examples.py +322 -0
- jac_client/tests/test_cl.py +480 -426
- jac_client/tests/test_create_jac_app.py +125 -133
- jac_client/tests/test_it.py +329 -0
- jac_client/tests/test_nested_file.py +374 -0
- {jac_client-0.2.0.dist-info → jac_client-0.2.2.dist-info}/METADATA +2 -2
- jac_client-0.2.2.dist-info/RECORD +171 -0
- jac_client-0.2.0.dist-info/RECORD +0 -72
- {jac_client-0.2.0.dist-info → jac_client-0.2.2.dist-info}/WHEEL +0 -0
- {jac_client-0.2.0.dist-info → jac_client-0.2.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Step 3: Styling Components
|
|
2
2
|
|
|
3
|
-
>
|
|
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
4
|
|
|
5
5
|
In this step, you'll learn how to style your components using inline CSS to make them look great!
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Part 1: Building the App
|
|
10
10
|
|
|
11
11
|
### Step 3.1: Style the TodoItem Component
|
|
12
12
|
|
|
@@ -162,11 +162,11 @@ cl {
|
|
|
162
162
|
|
|
163
163
|
---
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
**⏭ Want to skip the theory?** Jump to [Step 4: Todo UI](./step-04-todo-ui.md)
|
|
166
166
|
|
|
167
167
|
---
|
|
168
168
|
|
|
169
|
-
##
|
|
169
|
+
## Part 2: Understanding the Concepts
|
|
170
170
|
|
|
171
171
|
### What are Inline Styles?
|
|
172
172
|
|
|
@@ -223,14 +223,14 @@ text-align → "textAlign"
|
|
|
223
223
|
**Examples:**
|
|
224
224
|
|
|
225
225
|
```jac
|
|
226
|
-
#
|
|
226
|
+
# Correct (camelCase)
|
|
227
227
|
{
|
|
228
228
|
"backgroundColor": "#ffffff",
|
|
229
229
|
"fontSize": "16px",
|
|
230
230
|
"borderRadius": "8px"
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
#
|
|
233
|
+
# Wrong (kebab-case won't work)
|
|
234
234
|
{
|
|
235
235
|
"background-color": "#ffffff", # Error!
|
|
236
236
|
"font-size": "16px" # Error!
|
|
@@ -289,14 +289,14 @@ text-align → "textAlign"
|
|
|
289
289
|
All CSS values must be **strings** (in quotes):
|
|
290
290
|
|
|
291
291
|
```jac
|
|
292
|
-
#
|
|
292
|
+
# Correct
|
|
293
293
|
{
|
|
294
294
|
"padding": "20px",
|
|
295
295
|
"color": "#3b82f6",
|
|
296
296
|
"fontSize": "16px"
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
#
|
|
299
|
+
# Wrong (missing quotes)
|
|
300
300
|
{
|
|
301
301
|
"padding": 20px, # Error!
|
|
302
302
|
"color": #3b82f6, # Error!
|
|
@@ -395,19 +395,19 @@ def app() -> any {
|
|
|
395
395
|
|
|
396
396
|
---
|
|
397
397
|
|
|
398
|
-
##
|
|
398
|
+
## What You've Learned
|
|
399
399
|
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
-
|
|
403
|
-
-
|
|
404
|
-
-
|
|
405
|
-
-
|
|
406
|
-
-
|
|
400
|
+
- How to write inline styles in Jac
|
|
401
|
+
- Double curly braces `{{ }}` syntax
|
|
402
|
+
- camelCase property names
|
|
403
|
+
- Common CSS properties
|
|
404
|
+
- Conditional styling with ternary operator
|
|
405
|
+
- Flexbox basics for layout
|
|
406
|
+
- Reusing styles with variables
|
|
407
407
|
|
|
408
408
|
---
|
|
409
409
|
|
|
410
|
-
##
|
|
410
|
+
## Common Issues
|
|
411
411
|
|
|
412
412
|
### Issue: Styles not applying
|
|
413
413
|
|
|
@@ -422,10 +422,10 @@ def app() -> any {
|
|
|
422
422
|
**Cause**: Missing quotes around property names or values
|
|
423
423
|
|
|
424
424
|
```jac
|
|
425
|
-
#
|
|
425
|
+
# Wrong
|
|
426
426
|
{padding: 20px}
|
|
427
427
|
|
|
428
|
-
#
|
|
428
|
+
# Correct
|
|
429
429
|
{"padding": "20px"}
|
|
430
430
|
```
|
|
431
431
|
|
|
@@ -434,16 +434,16 @@ def app() -> any {
|
|
|
434
434
|
**Solution**: Convert kebab-case to camelCase
|
|
435
435
|
|
|
436
436
|
```jac
|
|
437
|
-
#
|
|
437
|
+
# Wrong
|
|
438
438
|
{"background-color": "#fff"}
|
|
439
439
|
|
|
440
|
-
#
|
|
440
|
+
# Correct
|
|
441
441
|
{"backgroundColor": "#fff"}
|
|
442
442
|
```
|
|
443
443
|
|
|
444
444
|
---
|
|
445
445
|
|
|
446
|
-
##
|
|
446
|
+
## Quick Exercise
|
|
447
447
|
|
|
448
448
|
Try adding a container with centered content:
|
|
449
449
|
|
|
@@ -471,8 +471,8 @@ This creates:
|
|
|
471
471
|
|
|
472
472
|
---
|
|
473
473
|
|
|
474
|
-
##
|
|
474
|
+
## Next Step
|
|
475
475
|
|
|
476
476
|
Great! Your components now look professional. Next, let's build the **complete Todo UI** with all the components working together!
|
|
477
477
|
|
|
478
|
-
|
|
478
|
+
**[Continue to Step 4: Todo UI](./step-04-todo-ui.md)**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Step 4: Building the Complete Todo UI
|
|
2
2
|
|
|
3
|
-
>
|
|
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
4
|
|
|
5
5
|
In this step, you'll put all your components together to create the full todo application interface!
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Part 1: Building the App
|
|
10
10
|
|
|
11
11
|
### Step 4.1: Complete App with All Components
|
|
12
12
|
|
|
@@ -205,11 +205,11 @@ cl {
|
|
|
205
205
|
|
|
206
206
|
---
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
**⏭ Want to skip the theory?** Jump to [Step 5: Local State](./step-05-local-state.md)
|
|
209
209
|
|
|
210
210
|
---
|
|
211
211
|
|
|
212
|
-
##
|
|
212
|
+
## Part 2: Understanding the Concepts
|
|
213
213
|
|
|
214
214
|
### Component Hierarchy
|
|
215
215
|
|
|
@@ -365,19 +365,19 @@ Using consistent colors makes your app look polished!
|
|
|
365
365
|
|
|
366
366
|
---
|
|
367
367
|
|
|
368
|
-
##
|
|
368
|
+
## What You've Learned
|
|
369
369
|
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
-
|
|
373
|
-
-
|
|
374
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
370
|
+
- Building a complete UI by composing components
|
|
371
|
+
- Component hierarchy and organization
|
|
372
|
+
- Container components that render lists
|
|
373
|
+
- Conditional rendering for empty states
|
|
374
|
+
- Centered card layout pattern
|
|
375
|
+
- Consistent spacing and colors
|
|
376
|
+
- Props flow from parent to child
|
|
377
377
|
|
|
378
378
|
---
|
|
379
379
|
|
|
380
|
-
##
|
|
380
|
+
## Common Issues
|
|
381
381
|
|
|
382
382
|
### Issue: Components overlapping
|
|
383
383
|
|
|
@@ -410,7 +410,7 @@ return <div>Show todos</div>; # This only runs if hasTodos is true
|
|
|
410
410
|
|
|
411
411
|
---
|
|
412
412
|
|
|
413
|
-
##
|
|
413
|
+
## Quick Exercise
|
|
414
414
|
|
|
415
415
|
Try customizing your app:
|
|
416
416
|
|
|
@@ -448,7 +448,7 @@ def app() -> any {
|
|
|
448
448
|
"color": "white",
|
|
449
449
|
"marginBottom": "20px"
|
|
450
450
|
}}>
|
|
451
|
-
<h1 style={{"margin": "0"}}
|
|
451
|
+
<h1 style={{"margin": "0"}}> Todo App</h1>
|
|
452
452
|
<p style={{"margin": "5px 0 0 0"}}>Stay organized!</p>
|
|
453
453
|
</div>
|
|
454
454
|
|
|
@@ -468,10 +468,10 @@ def app() -> any {
|
|
|
468
468
|
|
|
469
469
|
---
|
|
470
470
|
|
|
471
|
-
##
|
|
471
|
+
## Next Step
|
|
472
472
|
|
|
473
473
|
Excellent! Your UI is complete and looks great. But it's all static - clicking buttons does nothing!
|
|
474
474
|
|
|
475
475
|
In the next step, we'll add **state** to make your app interactive!
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
**[Continue to Step 5: Local State](./step-05-local-state.md)**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Step 5: Local State
|
|
2
2
|
|
|
3
|
-
>
|
|
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
4
|
|
|
5
5
|
In this step, you'll learn about **state** - the data that makes your app interactive and dynamic!
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Part 1: Building the App
|
|
10
10
|
|
|
11
11
|
### Step 5.1: First, Let's See Why Normal Variables Don't Work
|
|
12
12
|
|
|
@@ -267,11 +267,11 @@ cl {
|
|
|
267
267
|
|
|
268
268
|
---
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
**⏭ Want to skip the theory?** Jump to [Step 6: Event Handlers](./step-06-events.md)
|
|
271
271
|
|
|
272
272
|
---
|
|
273
273
|
|
|
274
|
-
##
|
|
274
|
+
## Part 2: Understanding the Concepts
|
|
275
275
|
|
|
276
276
|
### What is State?
|
|
277
277
|
|
|
@@ -380,7 +380,7 @@ let [name, setName] = useState("");
|
|
|
380
380
|
let [isOpen, setIsOpen] = useState(false);
|
|
381
381
|
let [todos, setTodos] = useState([]);
|
|
382
382
|
|
|
383
|
-
#
|
|
383
|
+
# Bad names
|
|
384
384
|
let [count, updateCount] = useState(0); # Inconsistent
|
|
385
385
|
let [x, y] = useState(0); # Not descriptive
|
|
386
386
|
```
|
|
@@ -413,11 +413,11 @@ items = [TodoItem(text=todo["text"]) for todo in todos]
|
|
|
413
413
|
**Never modify state directly:**
|
|
414
414
|
|
|
415
415
|
```jac
|
|
416
|
-
#
|
|
416
|
+
# WRONG - Never do this!
|
|
417
417
|
let [todos, setTodos] = useState([]);
|
|
418
418
|
todos.push(newTodo); # DON'T modify directly!
|
|
419
419
|
|
|
420
|
-
#
|
|
420
|
+
# CORRECT - Create new array
|
|
421
421
|
let [todos, setTodos] = useState([]);
|
|
422
422
|
setTodos(todos.concat([newTodo])); # Create new array
|
|
423
423
|
```
|
|
@@ -446,29 +446,29 @@ The child receives state but **cannot modify** the parent's state directly (we'l
|
|
|
446
446
|
|
|
447
447
|
---
|
|
448
448
|
|
|
449
|
-
##
|
|
449
|
+
## What You've Learned
|
|
450
450
|
|
|
451
|
-
-
|
|
452
|
-
-
|
|
453
|
-
-
|
|
454
|
-
-
|
|
455
|
-
-
|
|
456
|
-
-
|
|
457
|
-
-
|
|
451
|
+
- What state is and why we need it
|
|
452
|
+
- How to use the `useState` hook
|
|
453
|
+
- Creating multiple state variables
|
|
454
|
+
- State naming conventions
|
|
455
|
+
- Using `.map()` to render lists
|
|
456
|
+
- State is immutable (don't modify directly)
|
|
457
|
+
- Passing state to child components via props
|
|
458
458
|
|
|
459
459
|
---
|
|
460
460
|
|
|
461
|
-
##
|
|
461
|
+
## Common Issues
|
|
462
462
|
|
|
463
463
|
### Issue: UI not updating when state changes
|
|
464
464
|
|
|
465
465
|
**Check:** Are you modifying state directly?
|
|
466
466
|
|
|
467
467
|
```jac
|
|
468
|
-
#
|
|
468
|
+
# Wrong
|
|
469
469
|
todos.push(newTodo);
|
|
470
470
|
|
|
471
|
-
#
|
|
471
|
+
# Correct
|
|
472
472
|
setTodos(todos.concat([newTodo]));
|
|
473
473
|
```
|
|
474
474
|
|
|
@@ -477,10 +477,10 @@ setTodos(todos.concat([newTodo]));
|
|
|
477
477
|
**Check:** Did you initialize state as an array?
|
|
478
478
|
|
|
479
479
|
```jac
|
|
480
|
-
#
|
|
480
|
+
# Wrong
|
|
481
481
|
let [todos, setTodos] = useState(); # undefined
|
|
482
482
|
|
|
483
|
-
#
|
|
483
|
+
# Correct
|
|
484
484
|
let [todos, setTodos] = useState([]); # empty array
|
|
485
485
|
```
|
|
486
486
|
|
|
@@ -494,7 +494,7 @@ cl import from react {useState}
|
|
|
494
494
|
|
|
495
495
|
---
|
|
496
496
|
|
|
497
|
-
##
|
|
497
|
+
## Quick Exercise
|
|
498
498
|
|
|
499
499
|
Try adding more initial todos:
|
|
500
500
|
|
|
@@ -521,10 +521,10 @@ return <div>
|
|
|
521
521
|
|
|
522
522
|
---
|
|
523
523
|
|
|
524
|
-
##
|
|
524
|
+
## Next Step
|
|
525
525
|
|
|
526
526
|
Great! You now have state in your app, but you can't change it yet. Clicking buttons does nothing!
|
|
527
527
|
|
|
528
528
|
In the next step, we'll add **event handlers** to make your app fully interactive!
|
|
529
529
|
|
|
530
|
-
|
|
530
|
+
**[Continue to Step 6: Event Handlers](./step-06-events.md)**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Step 6: Event Handlers
|
|
2
2
|
|
|
3
|
-
>
|
|
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
4
|
|
|
5
5
|
In this step, you'll learn how to handle user interactions like clicks, typing, and key presses to make your app fully interactive!
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Part 1: Building the App
|
|
10
10
|
|
|
11
11
|
### Step 6.1: Handle Input Changes (onChange)
|
|
12
12
|
|
|
@@ -458,11 +458,11 @@ cl {
|
|
|
458
458
|
|
|
459
459
|
---
|
|
460
460
|
|
|
461
|
-
|
|
461
|
+
**⏭ Want to skip the theory?** Jump to [Step 7: Effects](./step-07-effects.md)
|
|
462
462
|
|
|
463
463
|
---
|
|
464
464
|
|
|
465
|
-
##
|
|
465
|
+
## Part 2: Understanding the Concepts
|
|
466
466
|
|
|
467
467
|
### What are Event Handlers?
|
|
468
468
|
|
|
@@ -555,10 +555,10 @@ When state updates, React re-renders the component with the new value!
|
|
|
555
555
|
**`.concat()` - Add items**
|
|
556
556
|
|
|
557
557
|
```jac
|
|
558
|
-
#
|
|
558
|
+
# Correct way to add
|
|
559
559
|
setTodos(todos.concat([newTodo]));
|
|
560
560
|
|
|
561
|
-
#
|
|
561
|
+
# Wrong (modifies original)
|
|
562
562
|
todos.push(newTodo);
|
|
563
563
|
setTodos(todos);
|
|
564
564
|
```
|
|
@@ -659,20 +659,20 @@ def removeItem(id: any) -> None {
|
|
|
659
659
|
|
|
660
660
|
---
|
|
661
661
|
|
|
662
|
-
##
|
|
662
|
+
## What You've Learned
|
|
663
663
|
|
|
664
|
-
-
|
|
665
|
-
-
|
|
666
|
-
-
|
|
667
|
-
-
|
|
668
|
-
-
|
|
669
|
-
-
|
|
670
|
-
-
|
|
671
|
-
-
|
|
664
|
+
- What event handlers are
|
|
665
|
+
- Common events (onClick, onChange, onKeyPress)
|
|
666
|
+
- Event handler syntax with lambda functions
|
|
667
|
+
- The event object (`e`)
|
|
668
|
+
- Passing functions as props
|
|
669
|
+
- Updating state in event handlers
|
|
670
|
+
- Array methods (concat, map, filter)
|
|
671
|
+
- Inline vs named functions
|
|
672
672
|
|
|
673
673
|
---
|
|
674
674
|
|
|
675
|
-
##
|
|
675
|
+
## Common Issues
|
|
676
676
|
|
|
677
677
|
### Issue: Event handler not firing
|
|
678
678
|
|
|
@@ -687,7 +687,7 @@ def removeItem(id: any) -> None {
|
|
|
687
687
|
- Is `onChange` calling the state setter?
|
|
688
688
|
|
|
689
689
|
```jac
|
|
690
|
-
#
|
|
690
|
+
# Correct
|
|
691
691
|
<input
|
|
692
692
|
value={text}
|
|
693
693
|
onChange={lambda e: any -> None {
|
|
@@ -695,7 +695,7 @@ def removeItem(id: any) -> None {
|
|
|
695
695
|
}}
|
|
696
696
|
/>
|
|
697
697
|
|
|
698
|
-
#
|
|
698
|
+
# Missing onChange
|
|
699
699
|
<input value={text} />
|
|
700
700
|
```
|
|
701
701
|
|
|
@@ -704,17 +704,17 @@ def removeItem(id: any) -> None {
|
|
|
704
704
|
**Check:** Are you creating a new array/object?
|
|
705
705
|
|
|
706
706
|
```jac
|
|
707
|
-
#
|
|
707
|
+
# Wrong (modifying original)
|
|
708
708
|
todos.push(newTodo);
|
|
709
709
|
setTodos(todos);
|
|
710
710
|
|
|
711
|
-
#
|
|
711
|
+
# Correct (creating new array)
|
|
712
712
|
setTodos(todos.concat([newTodo]));
|
|
713
713
|
```
|
|
714
714
|
|
|
715
715
|
---
|
|
716
716
|
|
|
717
|
-
##
|
|
717
|
+
## Quick Exercise
|
|
718
718
|
|
|
719
719
|
Try adding a "Clear All" button:
|
|
720
720
|
|
|
@@ -740,11 +740,10 @@ def clearCompleted() -> None {
|
|
|
740
740
|
|
|
741
741
|
---
|
|
742
742
|
|
|
743
|
-
##
|
|
743
|
+
## Next Step
|
|
744
744
|
|
|
745
745
|
Excellent! Your app is now fully interactive with local state. But when you refresh the page, all your todos disappear!
|
|
746
746
|
|
|
747
747
|
In the next step, we'll use **useEffect** to load data when the app starts!
|
|
748
748
|
|
|
749
|
-
|
|
750
|
-
|
|
749
|
+
**[Continue to Step 7: Effects](./step-07-effects.md)**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Step 7: Component Lifecycle with `useEffect`
|
|
2
2
|
|
|
3
|
-
>
|
|
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
4
|
|
|
5
5
|
In this step, you'll learn about **useEffect** - a way to run code when your component loads or when data changes!
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Part 1: Building the App
|
|
10
10
|
|
|
11
11
|
### Step 7.1: Understanding the Problem
|
|
12
12
|
|
|
@@ -91,7 +91,7 @@ cl {
|
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
**Try it!** Add some todos, then refresh the page - your todos persist!
|
|
94
|
+
**Try it!** Add some todos, then refresh the page - your todos persist!
|
|
95
95
|
|
|
96
96
|
### Step 7.5: Add Loading State
|
|
97
97
|
|
|
@@ -154,22 +154,22 @@ cl {
|
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
**⏭ Want to skip the theory?** Jump to [Step 8: Walkers](./step-08-walkers.md)
|
|
158
158
|
|
|
159
159
|
---
|
|
160
160
|
|
|
161
|
-
##
|
|
161
|
+
## Part 2: Understanding the Concepts
|
|
162
162
|
|
|
163
163
|
### What is `useEffect`?
|
|
164
164
|
|
|
165
165
|
`useEffect` lets you run **side effects** - code that affects things outside your component.
|
|
166
166
|
|
|
167
167
|
**Common side effects:**
|
|
168
|
-
-
|
|
169
|
-
-
|
|
168
|
+
- Fetching data from a server
|
|
169
|
+
- Saving data to localStorage
|
|
170
170
|
- ⏰ Setting up timers
|
|
171
|
-
-
|
|
172
|
-
-
|
|
171
|
+
- Logging analytics
|
|
172
|
+
- Subscribing to events
|
|
173
173
|
|
|
174
174
|
**Python analogy:**
|
|
175
175
|
|
|
@@ -368,31 +368,31 @@ useEffect(lambda -> None {
|
|
|
368
368
|
|
|
369
369
|
---
|
|
370
370
|
|
|
371
|
-
##
|
|
371
|
+
## What You've Learned
|
|
372
372
|
|
|
373
|
-
-
|
|
374
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
377
|
-
-
|
|
378
|
-
-
|
|
379
|
-
-
|
|
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
380
|
|
|
381
381
|
---
|
|
382
382
|
|
|
383
|
-
##
|
|
383
|
+
## Common Issues
|
|
384
384
|
|
|
385
385
|
### Issue: Effect runs too many times
|
|
386
386
|
|
|
387
387
|
**Check:** Is your dependency array correct?
|
|
388
388
|
|
|
389
389
|
```jac
|
|
390
|
-
#
|
|
390
|
+
# Wrong - runs on every render
|
|
391
391
|
useEffect(lambda -> None {
|
|
392
392
|
console.log(todos);
|
|
393
393
|
});
|
|
394
394
|
|
|
395
|
-
#
|
|
395
|
+
# Correct - runs only when todos change
|
|
396
396
|
useEffect(lambda -> None {
|
|
397
397
|
console.log(todos);
|
|
398
398
|
}, [todos]);
|
|
@@ -403,12 +403,12 @@ useEffect(lambda -> None {
|
|
|
403
403
|
**Check:** Did you include all dependencies?
|
|
404
404
|
|
|
405
405
|
```jac
|
|
406
|
-
#
|
|
406
|
+
# Wrong - missing todos dependency
|
|
407
407
|
useEffect(lambda -> None {
|
|
408
408
|
console.log(todos.length);
|
|
409
409
|
}, []);
|
|
410
410
|
|
|
411
|
-
#
|
|
411
|
+
# Correct - includes todos
|
|
412
412
|
useEffect(lambda -> None {
|
|
413
413
|
console.log(todos.length);
|
|
414
414
|
}, [todos]);
|
|
@@ -426,12 +426,12 @@ useEffect(lambda -> None {
|
|
|
426
426
|
**Cause:** Effect updates state, which triggers effect again
|
|
427
427
|
|
|
428
428
|
```jac
|
|
429
|
-
#
|
|
429
|
+
# Wrong - infinite loop!
|
|
430
430
|
useEffect(lambda -> None {
|
|
431
431
|
setTodos([...]); # This triggers effect again!
|
|
432
432
|
}, [todos]);
|
|
433
433
|
|
|
434
|
-
#
|
|
434
|
+
# Correct - run only once
|
|
435
435
|
useEffect(lambda -> None {
|
|
436
436
|
setTodos([...]);
|
|
437
437
|
}, []);
|
|
@@ -439,7 +439,7 @@ useEffect(lambda -> None {
|
|
|
439
439
|
|
|
440
440
|
---
|
|
441
441
|
|
|
442
|
-
##
|
|
442
|
+
## Quick Exercise
|
|
443
443
|
|
|
444
444
|
Try adding a "last saved" timestamp:
|
|
445
445
|
|
|
@@ -459,11 +459,10 @@ useEffect(lambda -> None {
|
|
|
459
459
|
|
|
460
460
|
---
|
|
461
461
|
|
|
462
|
-
##
|
|
462
|
+
## Next Step
|
|
463
463
|
|
|
464
464
|
Great! Your app now persists data with localStorage. But localStorage is only local to your browser!
|
|
465
465
|
|
|
466
466
|
In the next step, we'll add **real backend** using **walkers** so your data is stored on a server!
|
|
467
467
|
|
|
468
|
-
|
|
469
|
-
|
|
468
|
+
**[Continue to Step 8: Walkers](./step-08-walkers.md)**
|