Skip to content

Commit 8a8d8a1

Browse files
authored
feat: Complete rework (#38)
* feat: Complete rework, wip * Capture helper * unsubscribe queries on changes * concurrent execution * clean up * extended hello world * web time * docs * fix: Use forever tasks to clean up queries staled values * mutations, fixes and clean up * wip suspense support * clean up deps * fixes and composable queries * clean up * fixes and improvements * direct invalidation support * clean up * query docs * clean up * pass mutations keys when mutating * reactive context support for queries * reactive context support for mutations, and clean up * clean up warnings and deps * tweaks * mark as rc * use always shortest interval time * docs * docs * doc * fmt
1 parent d29e211 commit 8a8d8a1

21 files changed

+1780
-1166
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "dioxus-query"
33
description = "Fully-typed, async, reusable cached state management for Dioxus 🧬"
4-
version = "0.7.0"
4+
version = "0.8.0-rc.0"
55
edition = "2021"
66
license = "MIT"
77
authors = ["Marc Espín <[email protected]>"]
@@ -14,9 +14,9 @@ categories = ["gui", "asynchronous"]
1414
[dependencies]
1515
dioxus-lib = { version = "0.6", default-features = false, features = ["macro", "hooks", "signals"] }
1616
futures-util = "0.3.28"
17-
instant = { version = "0.1", features = ["wasm-bindgen"] }
1817
warnings = "0.2.1"
18+
tokio = { version = "^1", features = ["sync", "time"] }
1919

2020
[dev-dependencies]
2121
dioxus = { version = "0.6", features = ["desktop"] }
22-
tokio = { version = "1.29.1", features = ["time"] }
22+
tokio = { version = "^1", features = ["time"] }

README.md

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ See the [Docs](https://docs.rs/dioxus-query/latest/dioxus_query/) or join the [D
1212
- All renderers ([web](https://dioxuslabs.com/learn/0.4/getting_started/wasm), [desktop](https://dioxuslabs.com/learn/0.4/getting_started/desktop), [freya](https://github.com/marc2332/freya), etc)
1313
- Both WASM and native targets
1414

15+
## Features
16+
- [x] **Renderer-agnostic**
17+
- [x] **Queries** and **Mutations**
18+
- [x] **Fully typed**, no type erasing
19+
- [x] Invalidate queries **manually**
20+
- [x] Invalidate queries on **equality change**
21+
- [x] **Concurrent execution** of queries
22+
- [x] **Background interval re-execution** of queries
23+
- [x] **Opt-in in-memory cache** of queries results
24+
- [x] Works with ReactiveContext-powered hooks like **`use_effect` or `use_memo`**
25+
- [ ] On window/tab focus invalidation
26+
27+
1528
## Installation
1629

1730
Install the latest release:
@@ -21,79 +34,66 @@ cargo add dioxus-query
2134

2235
## Example
2336

37+
Run manually:
2438
```bash
25-
cargo run --example simple
39+
cargo run --example hello_world
2640
```
2741

28-
## Usage
29-
42+
Code:
3043
```rust
31-
#[derive(Clone, PartialEq, Eq, Hash)]
32-
enum QueryKey {
33-
User(usize),
34-
}
44+
#[derive(Clone, PartialEq, Eq)]
45+
struct FancyClient;
3546

36-
#[derive(Debug)]
37-
enum QueryError {
38-
UserNotFound(usize),
39-
Unknown
47+
impl FancyClient {
48+
pub fn name(&self) -> &'static str {
49+
"Marc"
50+
}
4051
}
4152

42-
#[derive(PartialEq, Debug)]
43-
enum QueryValue {
44-
UserName(String),
45-
}
53+
#[derive(Clone, PartialEq, Hash, Eq)]
54+
struct GetUserName(Captured<FancyClient>);
55+
56+
impl QueryCapability for GetUserName {
57+
type Ok = String;
58+
type Err = ();
59+
type Keys = usize;
4660

47-
async fn fetch_user(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryError> {
48-
if let Some(QueryKey::User(id)) = keys.first() {
49-
println!("Fetching user {id}");
50-
sleep(Duration::from_millis(1000)).await;
51-
match id {
52-
0 => Ok(QueryValue::UserName("Marc".to_string())),
53-
_ => Err(QueryError::UserNotFound(*id)),
61+
async fn run(&self, user_id: &Self::Keys) -> Result<Self::Ok, Self::Err> {
62+
println!("Fetching name of user {user_id}");
63+
sleep(Duration::from_millis(650)).await;
64+
match user_id {
65+
0 => Ok(self.0.name().to_string()),
66+
_ => Err(()),
5467
}
55-
} else {
56-
Err(QueryError::Unknown)
5768
}
5869
}
5970

6071
#[allow(non_snake_case)]
6172
#[component]
6273
fn User(id: usize) -> Element {
63-
let value = use_get_query([QueryKey::User(id)], fetch_user);
74+
let user_name = use_query(Query::new(id, GetUserName(Captured(FancyClient))));
6475

65-
rsx!( p { "{value.result().value():?}" } )
76+
rsx!(
77+
p { "{user_name.read().state():?}" }
78+
)
6679
}
6780

6881
fn app() -> Element {
69-
let client = use_init_query_client::<QueryValue, QueryError, QueryKey>();
70-
71-
let onclick = move |_| {
72-
client.invalidate_queries(&[QueryKey::User(0)]);
82+
let refresh = move |_| async move {
83+
QueriesStorage::<GetUserName>::invalidate_matching(0).await;
7384
};
7485

7586
rsx!(
7687
User { id: 0 }
77-
button { onclick, label { "Refresh" } }
88+
User { id: 0 }
89+
button { onclick: refresh, label { "Refresh" } }
7890
)
7991
}
8092
```
8193

82-
## Features
83-
- [x] Renderer-agnostic
84-
- [x] Queries and mutations
85-
- [x] Typed Mutations, Query keys, Errors and Values
86-
- [x] Invalidate queries manually
87-
- [x] Invalidate queries when keys change
88-
- [x] Concurrent and batching of queries
89-
- [x] Concurrent mutations
90-
- [ ] Background interval invalidation
91-
- [ ] On window focus invalidation
92-
93-
9494
## To Do
9595
- Tests
96-
- Documentation
96+
- Improved documentation
9797
- Real-world examples
9898

9999
MIT License

examples/complex_queries.rs

Lines changed: 0 additions & 84 deletions
This file was deleted.

examples/composable.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#![cfg_attr(
2+
all(not(debug_assertions), target_os = "windows"),
3+
windows_subsystem = "windows"
4+
)]
5+
6+
use dioxus_query::prelude::*;
7+
use std::time::Duration;
8+
use tokio::time::sleep;
9+
10+
use dioxus::prelude::*;
11+
12+
fn main() {
13+
launch(app);
14+
}
15+
16+
#[derive(Clone, PartialEq, Eq)]
17+
struct FancyClient;
18+
19+
impl FancyClient {
20+
pub fn name(&self) -> &'static str {
21+
"Marc"
22+
}
23+
24+
pub fn age(&self) -> u8 {
25+
123
26+
}
27+
}
28+
29+
#[derive(Clone, PartialEq, Hash, Eq)]
30+
struct GetUserName(Captured<FancyClient>);
31+
32+
impl QueryCapability for GetUserName {
33+
type Ok = String;
34+
type Err = ();
35+
type Keys = usize;
36+
37+
async fn run(&self, user_id: &Self::Keys) -> Result<Self::Ok, Self::Err> {
38+
println!("Fetching name of user {user_id}");
39+
sleep(Duration::from_millis(650)).await;
40+
match user_id {
41+
0 => Ok(self.0.name().to_string()),
42+
_ => Err(()),
43+
}
44+
}
45+
}
46+
47+
#[derive(Clone, PartialEq, Hash, Eq)]
48+
struct GetUserInfo(Captured<FancyClient>);
49+
50+
impl QueryCapability for GetUserInfo {
51+
type Ok = (String, u8);
52+
type Err = ();
53+
type Keys = usize;
54+
55+
async fn run(&self, user_id: &Self::Keys) -> Result<Self::Ok, Self::Err> {
56+
let name = QueriesStorage::get(
57+
GetQuery::new(*user_id, GetUserName(self.0.clone()))
58+
.stale_time(Duration::from_secs(30))
59+
.clean_time(Duration::from_secs(30)),
60+
)
61+
.await;
62+
let name = name.as_settled().clone()?;
63+
println!("Fetching age of user {user_id}");
64+
sleep(Duration::from_millis(1000)).await;
65+
match user_id {
66+
0 => Ok((name, self.0.age())),
67+
_ => Err(()),
68+
}
69+
}
70+
}
71+
72+
#[allow(non_snake_case)]
73+
#[component]
74+
fn User(id: usize) -> Element {
75+
let fancy_client = FancyClient;
76+
77+
let user_info = use_query(
78+
Query::new(id, GetUserInfo(Captured(fancy_client))).stale_time(Duration::from_secs(10)),
79+
);
80+
81+
println!("Rendering user {id}");
82+
83+
rsx!(
84+
p { "{user_info.read().state():?}" }
85+
)
86+
}
87+
88+
fn app() -> Element {
89+
let refresh = move |_| async move {
90+
QueriesStorage::<GetUserInfo>::invalidate_matching(0).await;
91+
};
92+
93+
rsx!(
94+
User { id: 0 }
95+
User { id: 0 }
96+
button { onclick: refresh, label { "Refresh" } }
97+
)
98+
}

examples/direct_invalidation.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#![cfg_attr(
2+
all(not(debug_assertions), target_os = "windows"),
3+
windows_subsystem = "windows"
4+
)]
5+
6+
use dioxus_query::prelude::*;
7+
use std::time::{SystemTime, UNIX_EPOCH};
8+
9+
use dioxus::prelude::*;
10+
11+
fn main() {
12+
launch(app);
13+
}
14+
15+
#[derive(Clone, PartialEq, Hash, Eq)]
16+
struct GetTime;
17+
18+
impl QueryCapability for GetTime {
19+
type Ok = SystemTime;
20+
type Err = ();
21+
type Keys = ();
22+
23+
async fn run(&self, _: &Self::Keys) -> Result<Self::Ok, Self::Err> {
24+
Ok(SystemTime::now())
25+
}
26+
}
27+
28+
fn app() -> Element {
29+
let time = use_query(Query::new((), GetTime));
30+
31+
let refresh = move |_| {
32+
time.invalidate();
33+
};
34+
35+
let time = time
36+
.read()
37+
.state()
38+
.ok()
39+
.map(|time| time.duration_since(UNIX_EPOCH));
40+
41+
rsx!(
42+
p {
43+
"{time:?}"
44+
}
45+
button { onclick: refresh, label { "Refresh" } }
46+
)
47+
}

0 commit comments

Comments
 (0)