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.
- jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
- jac_client/examples/all-in-one/src/app.jac +841 -0
- jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
- jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
- jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +7 -7
- jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/basic-auth/src/app.jac +377 -0
- jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
- jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
- jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
- jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
- jac_client/examples/little-x/src/submit-button.jac +16 -0
- jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
- jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
- jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
- jac_client/examples/ts-support/src/app.jac +35 -0
- jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
- jac_client/plugin/cli.jac +547 -0
- jac_client/plugin/client.jac +52 -0
- jac_client/plugin/client_runtime.cl.jac +38 -0
- jac_client/plugin/impl/client.impl.jac +134 -0
- jac_client/plugin/impl/client_runtime.impl.jac +177 -0
- jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
- jac_client/plugin/plugin_config.jac +195 -0
- jac_client/plugin/src/__init__.jac +20 -0
- jac_client/plugin/src/asset_processor.jac +33 -0
- jac_client/plugin/src/babel_processor.jac +18 -0
- jac_client/plugin/src/compiler.jac +66 -0
- jac_client/plugin/src/config_loader.jac +32 -0
- jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
- jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
- jac_client/plugin/src/impl/compiler.impl.jac +251 -0
- jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
- jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
- jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
- jac_client/plugin/src/import_processor.jac +19 -0
- jac_client/plugin/src/jac_to_js.jac +35 -0
- jac_client/plugin/src/package_installer.jac +26 -0
- jac_client/plugin/src/vite_bundler.jac +36 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/conftest.py +281 -0
- jac_client/tests/fixtures/basic-app/app.jac +2 -2
- jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
- jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
- jac_client/tests/fixtures/js_import/app.jac +5 -5
- jac_client/tests/fixtures/spawn_test/app.jac +7 -7
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- jac_client/tests/test_cli.py +755 -0
- jac_client/tests/test_it.py +347 -67
- {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/METADATA +30 -24
- jac_client-0.2.6.dist-info/RECORD +74 -0
- {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/WHEEL +2 -1
- jac_client-0.2.6.dist-info/entry_points.txt +4 -0
- jac_client-0.2.6.dist-info/top_level.txt +1 -0
- jac_client/docs/README.md +0 -689
- jac_client/docs/advanced-state.md +0 -1265
- jac_client/docs/asset-serving/intro.md +0 -209
- jac_client/docs/assets/pipe_line-v2.svg +0 -32
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/app.jac.md +0 -121
- jac_client/docs/file-system/backend-frontend.md +0 -217
- jac_client/docs/file-system/intro.md +0 -72
- jac_client/docs/file-system/nested-imports.md +0 -348
- jac_client/docs/guide-example/intro.md +0 -115
- jac_client/docs/guide-example/step-01-setup.md +0 -270
- jac_client/docs/guide-example/step-02-components.md +0 -416
- jac_client/docs/guide-example/step-03-styling.md +0 -478
- jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
- jac_client/docs/guide-example/step-05-local-state.md +0 -530
- jac_client/docs/guide-example/step-06-events.md +0 -749
- jac_client/docs/guide-example/step-07-effects.md +0 -468
- jac_client/docs/guide-example/step-08-walkers.md +0 -534
- jac_client/docs/guide-example/step-09-authentication.md +0 -586
- jac_client/docs/guide-example/step-10-routing.md +0 -539
- jac_client/docs/guide-example/step-11-final.md +0 -963
- jac_client/docs/imports.md +0 -1141
- jac_client/docs/lifecycle-hooks.md +0 -773
- jac_client/docs/routing.md +0 -659
- jac_client/docs/styling/intro.md +0 -249
- jac_client/docs/styling/js-styling.md +0 -367
- jac_client/docs/styling/material-ui.md +0 -341
- jac_client/docs/styling/pure-css.md +0 -299
- jac_client/docs/styling/sass.md +0 -403
- jac_client/docs/styling/styled-components.md +0 -395
- jac_client/docs/styling/tailwind.md +0 -298
- jac_client/examples/all-in-one/.babelrc +0 -9
- jac_client/examples/all-in-one/README.md +0 -16
- jac_client/examples/all-in-one/app.jac +0 -426
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/package.json +0 -29
- jac_client/examples/all-in-one/styles.css +0 -26
- jac_client/examples/all-in-one/vite.config.js +0 -28
- jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
- jac_client/examples/asset-serving/css-with-image/README.md +0 -91
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +0 -28
- jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
- jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
- jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
- jac_client/examples/asset-serving/image-asset/README.md +0 -119
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +0 -28
- jac_client/examples/asset-serving/image-asset/styles.css +0 -26
- jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
- jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
- jac_client/examples/asset-serving/import-alias/README.md +0 -83
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +0 -28
- jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
- jac_client/examples/basic/.babelrc +0 -9
- jac_client/examples/basic/README.md +0 -16
- jac_client/examples/basic/package.json +0 -27
- jac_client/examples/basic/vite.config.js +0 -27
- jac_client/examples/basic-auth/.babelrc +0 -9
- jac_client/examples/basic-auth/README.md +0 -16
- jac_client/examples/basic-auth/app.jac +0 -308
- jac_client/examples/basic-auth/package.json +0 -27
- jac_client/examples/basic-auth/vite.config.js +0 -27
- jac_client/examples/basic-auth-with-router/.babelrc +0 -9
- jac_client/examples/basic-auth-with-router/README.md +0 -60
- jac_client/examples/basic-auth-with-router/package.json +0 -28
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
- jac_client/examples/basic-full-stack/.babelrc +0 -9
- jac_client/examples/basic-full-stack/README.md +0 -18
- jac_client/examples/basic-full-stack/package.json +0 -28
- jac_client/examples/basic-full-stack/vite.config.js +0 -27
- jac_client/examples/css-styling/js-styling/.babelrc +0 -9
- jac_client/examples/css-styling/js-styling/README.md +0 -183
- jac_client/examples/css-styling/js-styling/package.json +0 -28
- jac_client/examples/css-styling/js-styling/styles.js +0 -100
- jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
- jac_client/examples/css-styling/material-ui/.babelrc +0 -9
- jac_client/examples/css-styling/material-ui/README.md +0 -16
- jac_client/examples/css-styling/material-ui/package.json +0 -32
- jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
- jac_client/examples/css-styling/pure-css/.babelrc +0 -9
- jac_client/examples/css-styling/pure-css/README.md +0 -16
- jac_client/examples/css-styling/pure-css/package.json +0 -28
- jac_client/examples/css-styling/pure-css/styles.css +0 -111
- jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
- jac_client/examples/css-styling/sass-example/.babelrc +0 -9
- jac_client/examples/css-styling/sass-example/README.md +0 -16
- jac_client/examples/css-styling/sass-example/package.json +0 -29
- jac_client/examples/css-styling/sass-example/styles.scss +0 -153
- jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
- jac_client/examples/css-styling/styled-components/.babelrc +0 -9
- jac_client/examples/css-styling/styled-components/README.md +0 -16
- jac_client/examples/css-styling/styled-components/package.json +0 -29
- jac_client/examples/css-styling/styled-components/styled.js +0 -90
- jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
- jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
- jac_client/examples/css-styling/tailwind-example/README.md +0 -16
- jac_client/examples/css-styling/tailwind-example/global.css +0 -1
- jac_client/examples/css-styling/tailwind-example/package.json +0 -30
- jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
- jac_client/examples/full-stack-with-auth/.babelrc +0 -9
- jac_client/examples/full-stack-with-auth/README.md +0 -16
- jac_client/examples/full-stack-with-auth/package.json +0 -28
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
- jac_client/examples/little-x/package.json +0 -23
- jac_client/examples/little-x/submit-button.jac +0 -8
- jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-advance/README.md +0 -77
- jac_client/examples/nested-folders/nested-advance/package.json +0 -29
- jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
- jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-basic/README.md +0 -183
- jac_client/examples/nested-folders/nested-basic/app.js +0 -7
- jac_client/examples/nested-folders/nested-basic/package.json +0 -28
- jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
- jac_client/examples/with-router/.babelrc +0 -9
- jac_client/examples/with-router/README.md +0 -17
- jac_client/examples/with-router/package.json +0 -28
- jac_client/examples/with-router/vite.config.js +0 -27
- jac_client/plugin/cli.py +0 -244
- jac_client/plugin/client.py +0 -152
- jac_client/plugin/client_runtime.jac +0 -234
- jac_client/plugin/vite_client_bundle.py +0 -503
- jac_client/tests/fixtures/js_import/utils.js +0 -21
- jac_client/tests/fixtures/package-lock.json +0 -329
- jac_client/tests/fixtures/package.json +0 -11
- jac_client/tests/test_asset_examples.py +0 -322
- jac_client/tests/test_cl.py +0 -530
- jac_client/tests/test_create_jac_app.py +0 -131
- jac_client/tests/test_nested_file.py +0 -374
- jac_client-0.2.2.dist-info/RECORD +0 -171
- 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
|
-
```
|