You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -10,17 +10,47 @@ Applies to Dioxus **fullstack** projects (shared code server/web). Use these whe
10
10
### 0) Shared models (client + server)
11
11
-**Rule**: If a struct is used both on server **and** client (e.g., returned from a server function and rendered in UI) → put it in `src/models`.
12
12
-**Rule**: If a struct is only used inside a server function (e.g., parsing an external API response) → keep it private in the `src/api/*.rs` file, gated with `#[cfg(feature = "server")]`.
13
+
-**Naming**: Models returned from server functions (HTTP/server fn boundary) use the `HttpModel` suffix: `BalanceHistoryHttpModel`, `InstrumentHttpModel`.
14
+
-**One file per type**: each model lives in its own file (`models/balance_history.rs`, `models/instrument.rs`).
15
+
-`mod.rs` uses the standard re-export pattern: `mod x; pub use x::*;`
16
+
- In components: `use crate::models::*;` — never enumerate types explicitly.
13
17
- Derive `Serialize`/`Deserialize` for anything crossing the wire; keep structs minimal and web-safe.
14
-
- If a model is used in `http_route` responses (server-only), also derive `MyHttpObjectStructure` when required by your HTTP doc tooling; otherwise `Serialize`/`Deserialize` is enough.
15
18
-**Examples**:
16
-
-`BinanceInstrumentCheckResponse` → in `src/models` (returned to client, shown in dialog)
17
-
-`BinanceExchangeInfo`, `BinanceSymbolInfo` → private in `src/api/binance.rs` with `#[cfg(feature = "server")]` (only used to parse Binance API response)
18
-
-`InputValue<T>` → in `src/models` (shared validation helper used in components)
19
+
-`BalanceHistoryHttpModel` → in `src/models/balance_history.rs` (returned to client, shown in UI)
20
+
-`BinanceExchangeInfo` → private in `src/api/binance.rs` with `#[cfg(feature = "server")]` (only used to parse external API response)
21
+
22
+
### 0.1) Mappers (server-only)
23
+
- Mappers know about gRPC/server contracts — they live in `src/server/` (not visible to client code).
24
+
-**1:1 mapping** → `impl From<GrpcModel> for HttpModel`, then in api: `.map(|i| i.into()).collect()`
25
+
-**Complex mapping** (multiple structs, logic) → a mapper function in `src/server/mappers/`
26
+
27
+
```rust
28
+
// ✅ 1:1 — impl From in server/mappers/ or alongside the model
### 6) Data loading — DataState + `get_data` pattern
165
+
166
+
**Every** piece of async data uses `DataState<T>` and a `get_data` helper function. Never call API directly in a component body or trigger loading via manual `loading: bool` fields.
167
+
168
+
The helper handles all four states and triggers loading automatically on first render (`None` branch spawns the fetch):
169
+
170
+
```rust
171
+
#[derive(Default)]
172
+
structMyListState {
173
+
data:DataState<Vec<MyHttpModel>>,
174
+
}
175
+
176
+
#[component]
177
+
fnMyList(some_id:i64) ->Element {
178
+
letcs=use_signal(MyListState::default);
179
+
letcs_ra=cs.read();
180
+
181
+
letitems=matchget_my_data(cs, &cs_ra, some_id) {
182
+
Ok(d) =>d,
183
+
Err(el) =>returnel,
184
+
};
185
+
rsx! { /* render items */ }
186
+
}
151
187
152
-
### 7) Server functions as API boundary (fullstack)
**Forced reload after mutation**: call `.reset()` on `DataState` — it returns to `None`, and the next render triggers a fresh load automatically.
212
+
213
+
```rust
214
+
// After save/delete — just reset, get_data will reload on next render
215
+
cs.write().data.reset();
216
+
```
217
+
218
+
If the reset is triggered from **outside** the component (e.g., parent after a mutation), the `DataState` must be accessible from the caller: either lift it into the parent's state or pass `Signal<ChildState>` as a prop.
219
+
220
+
### 7) Component structure for pages with tabs and lists
221
+
222
+
- Each **tab** is its own `#[component]` receiving an ID prop and owning its `DataState`.
223
+
- Each **list within a tab** (e.g., active positions + pending orders) is also a separate component with its own `DataState`.
224
+
-**Page-level state** holds only UI: current tab, search input, selected item, flags — **never data arrays**.
225
+
226
+
```
227
+
PageComponent ← PageState: input, selected item, tab, ui flags only
228
+
└── ContentComponent
229
+
├── TabA { id } ← own State + DataState, get_data pattern
230
+
│ ├── ListOne { id } ← own State + DataState
231
+
│ └── ListTwo { id } ← own State + DataState
232
+
├── TabB { id } ← own State + DataState
233
+
└── TabC { id, cs } ← uses parent Signal if parent needs to trigger reset
234
+
```
235
+
236
+
When a child tab needs to be reloaded from the parent (e.g., balance history after deposit), either:
237
+
- Lift the `DataState` into the parent's state and pass `Signal<PageState>` to the tab, or
238
+
- Pass `Signal<ChildState>` as a prop so the parent can call `.reset()` directly.
239
+
240
+
### 8) Server functions as API boundary (fullstack)
153
241
- Use Dioxus fullstack server functions (`#[get]`, `#[post]` in `src/api/*`) for all client <-> server calls; they compile to RPCs on web and direct calls on server.
- Prefer `Result<T, ServerFnError>`; let the client handle loading/error rendering via `DataState`.
@@ -165,10 +253,10 @@ Applies to Dioxus **fullstack** projects (shared code server/web). Use these whe
165
253
}
166
254
```
167
255
168
-
### 8) Dialog template usage
256
+
### 9) Dialog template usage
169
257
- Provide `header`, optional `header_content`, main `content`, optional `ok_button`, and `allocate_max_space` when needed.
170
258
- Cancel/close is built in; for custom OK, pass a button element to `ok_button`.
171
-
- The close “X” uses the dialog context; no per-dialog wiring required.
259
+
- The close "X" uses the dialog context; no per-dialog wiring required.
172
260
-**Example: template with OK**
173
261
```rust
174
262
DialogTemplate {
@@ -187,7 +275,7 @@ Applies to Dioxus **fullstack** projects (shared code server/web). Use these whe
187
275
}
188
276
```
189
277
190
-
### 9) Signal handling tips
278
+
### 10) Signal handling tips
191
279
- Signals are `Copy`; capture once in handlers. Only clone when moving into async blocks.
192
280
- Avoid nested `cs.clone()` layers unless a separate handle is truly needed.
193
281
- Read with `.read()` for an immutable snapshot; write with `.write()` to mutate.
@@ -203,11 +291,11 @@ Applies to Dioxus **fullstack** projects (shared code server/web). Use these whe
203
291
};
204
292
```
205
293
206
-
### 9.1) Client-side "now" date/time
207
-
-**Rule**: If “now” date/time must be resolved on the **client side** in a Dioxus fullstack app, use `dioxus_utils::now_date_time()`.
294
+
### 10.1) Client-side "now" date/time
295
+
-**Rule**: If "now" date/time must be resolved on the **client side** in a Dioxus fullstack app, use `dioxus_utils::now_date_time()`.
208
296
- This avoids server-side resolution and keeps client-local time semantics correct.
209
297
210
-
### 10) Status messaging
298
+
### 11) Status messaging
211
299
- Store transient statuses (availability checks, errors) in the form state and render inline near the related control.
212
300
- Clear stale statuses when the input they depend on changes.
213
301
-**Example**
@@ -218,4 +306,120 @@ Applies to Dioxus **fullstack** projects (shared code server/web). Use these whe
218
306
{ status.message.unwrap_or_else(||"OK".into()) }
219
307
}
220
308
}
221
-
```
309
+
```
310
+
311
+
### 12) `use` imports in server functions — always inside the function body
312
+
313
+
In server functions (`#[get]`, `#[post]`), put **all `use` imports inside the function body**, not at the file level. Top-level imports cause `unused import` warnings on the web target where `feature = "server"` is disabled.
314
+
315
+
```rust
316
+
// ✅ CORRECT — imports inside function, no warnings on web target
Use when a parent action (e.g. deposit, save) must trigger a child component to reload its own `DataState`, and the child manages its state independently (not in the parent's state).
336
+
337
+
`NotifyChildComponent<TValue>` is in `dioxus_utils`. It wraps a `Signal<Option<TValue>>` and is `Copy + Clone`, so it can be passed as a component prop.
338
+
339
+
**Parent** — create once with `new()`, pass to child as prop, call `notify_other_components(value)` after mutation:
**Child** — call `on_notify(callback)` as a hook. Internally sets up `use_effect` that fires when the signal changes, consumes the value, and runs the callback:
cs.write().data.reset(); // triggers get_data reload on next render
361
+
});
362
+
363
+
letcs_ra=cs.read();
364
+
letitems=matchget_data(cs, &cs_ra) { ... };
365
+
rsx! { /* render */ }
366
+
}
367
+
```
368
+
369
+
**Key properties:**
370
+
-`NotifyChildComponent` holds `Signal<Option<TValue>>` — `Copy`, safe to pass as prop
371
+
-`on_notify` must be called at component top level (it wraps `use_effect`)
372
+
- The notification is consumed once — child's `use_effect` fires, clears the value, runs callback
373
+
- After `.reset()` on `DataState`, the `get_data` helper sees `None` and spawns a reload automatically
374
+
375
+
### 14) `dialog_template` / `dialog_template_ex` — standard dialog wrapper
376
+
377
+
All dialogs use `dialog_template` (or `dialog_template_ex` for custom size) instead of inlining modal HTML. This keeps dialog structure consistent and eliminates boilerplate.
0 commit comments