Skip to content

Commit

Permalink
feat: Simplification (#26)
Browse files Browse the repository at this point in the history
* feat: Simplification

* chore: Update readme
  • Loading branch information
marc2332 authored Mar 2, 2025
1 parent 842a783 commit 08e319d
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 157 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "dioxus-query"
description = "Fully-typed, async, reusable cached state management for Dioxus 🧬"
version = "0.6.1"
version = "0.7.0"
edition = "2021"
license = "MIT"
authors = ["Marc Espín <mespinsanz@gmail.com>"]
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

See the [Docs](https://docs.rs/dioxus-query/latest/dioxus_query/) or join the [Discord](https://discord.gg/gwuU8vGRPr).

⚠️ **Work in progress ⚠️**

## Support

- **Dioxus v0.6** 🧬
Expand Down Expand Up @@ -54,9 +52,8 @@ async fn fetch_user(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryError>
0 => Ok(QueryValue::UserName("Marc".to_string())),
_ => Err(QueryError::UserNotFound(*id)),
}
.into()
} else {
QueryResult::Err(QueryError::Unknown)
Err(QueryError::Unknown)
}
}

Expand All @@ -69,11 +66,10 @@ fn User(id: usize) -> Element {
}

fn app() -> Element {
use_init_query_client::<QueryValue, QueryError, QueryKey>();
let client = use_query_client::<QueryValue, QueryError, QueryKey>();
let client = use_init_query_client::<QueryValue, QueryError, QueryKey>();

let onclick = move |_| {
client.invalidate_query(QueryKey::User(0));
client.invalidate_queries(&[QueryKey::User(0)]);
};

rsx!(
Expand All @@ -91,6 +87,9 @@ fn app() -> Element {
- [x] Invalidate queries when keys change
- [x] Concurrent and batching of queries
- [x] Concurrent mutations
- [ ] Background interval invalidation
- [ ] On window focus invalidation


## To Do
- Tests
Expand Down
7 changes: 3 additions & 4 deletions examples/complex_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ async fn fetch_user(keys: Vec<QueryKey>) -> QueryResult<QueryValue, ()> {
1 => Ok(QueryValue::UserName("Evan".to_string())),
_ => Err(()),
}
.into()
} else {
QueryResult::Err(())
Err(())
}
}

Expand Down Expand Up @@ -68,12 +67,12 @@ fn app() -> Element {
let client = use_query_client::<QueryValue, (), QueryKey>();

let refresh_0 = move |_| {
client.invalidate_query(QueryKey::User(0));
client.invalidate_queries(&[QueryKey::User(0)]);
};

let refresh_1 = move |_| client.invalidate_queries(&[QueryKey::User(1)]);

let refresh_all = move |_| client.invalidate_query(QueryKey::Users);
let refresh_all = move |_| client.invalidate_queries(&[QueryKey::Users]);

rsx!(
User { id: 0 }
Expand Down
4 changes: 2 additions & 2 deletions examples/mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum MutationValue {
async fn update_user((id, _name): (usize, String)) -> MutationResult<MutationValue, MutationError> {
println!("Mutating user");
sleep(Duration::from_millis(1000)).await;
Ok(MutationValue::UserUpdated(id)).into()
Ok(MutationValue::UserUpdated(id))
}

#[allow(non_snake_case)]
Expand All @@ -33,7 +33,7 @@ fn User(id: usize) -> Element {
let mutate = use_mutation(update_user);

let onclick = move |_| {
mutate.mutate((0, "Not Marc".to_string()));
mutate.mutate((id, "Not Marc".to_string()));
};

println!("Rendering user {id}");
Expand Down
34 changes: 25 additions & 9 deletions examples/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn main() {
#[derive(Clone, PartialEq, Eq, Hash)]
enum QueryKey {
User(usize),
Other,
}

#[derive(PartialEq, Debug)]
Expand All @@ -38,9 +39,8 @@ async fn fetch_user(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryError>
0 => Ok(QueryValue::UserName("Marc".to_string())),
_ => Err(QueryError::UserNotFound(*id)),
}
.into()
} else {
QueryResult::Err(QueryError::Unknown)
Err(QueryError::Unknown)
}
}

Expand All @@ -52,17 +52,30 @@ async fn fetch_user_age(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErr
0 => Ok(QueryValue::UserAge(0)),
_ => Err(QueryError::UserNotFound(*id)),
}
.into()
} else {
QueryResult::Err(QueryError::Unknown)
Err(QueryError::Unknown)
}
}

#[derive(Debug)]
enum MutationError {}

#[derive(PartialEq, Debug)]
enum MutationValue {
UserUpdated(usize),
}

async fn update_user((id, _name): (usize, String)) -> MutationResult<MutationValue, MutationError> {
println!("Mutating user");
sleep(Duration::from_millis(1000)).await;
Ok(MutationValue::UserUpdated(id))
}

#[allow(non_snake_case)]
#[component]
fn User(id: usize) -> Element {
let user_name = use_get_query([QueryKey::User(id)], fetch_user);
let user_age = use_get_query([QueryKey::User(id)], fetch_user_age);
let user_name = use_get_query([QueryKey::User(id), QueryKey::Other], fetch_user);
let user_age = use_get_query([QueryKey::User(id), QueryKey::Other], fetch_user_age);

println!("Rendering user {id}");

Expand All @@ -73,10 +86,13 @@ fn User(id: usize) -> Element {
}

fn app() -> Element {
use_init_query_client::<QueryValue, QueryError, QueryKey>();
let client = use_query_client::<QueryValue, QueryError, QueryKey>();
let mutate = use_mutation(update_user);
let client = use_init_query_client::<QueryValue, QueryError, QueryKey>();

let refresh = move |_| client.invalidate_query(QueryKey::User(0));
let refresh = move |_| async move {
mutate.mutate_async((0, "Not Marc".to_string())).await;
client.invalidate_queries(&[QueryKey::User(0)]);
};

rsx!(
User { id: 0 }
Expand Down
37 changes: 21 additions & 16 deletions src/cached_result.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
use instant::Instant;
use std::{fmt::Debug, ops::Deref, time::Duration};

use crate::result::QueryResult;

const STALE_TIME: u64 = 100;
use crate::result::QueryState;

/// Cached result.
#[derive(Debug, Clone, PartialEq)]
pub struct CachedResult<T, E> {
pub(crate) value: QueryResult<T, E>,
pub(crate) value: QueryState<T, E>,
pub(crate) instant: Option<Instant>,
pub(crate) has_been_queried: bool,
pub(crate) has_been_loaded: bool,
}

impl<T, E> CachedResult<T, E> {
pub fn new(value: QueryResult<T, E>) -> Self {
pub fn new(value: QueryState<T, E>) -> Self {
Self {
value,
..Default::default()
}
}

/// Get this result's value
pub fn value(&self) -> &QueryResult<T, E> {
pub fn value(&self) -> &QueryState<T, E> {
&self.value
}

/// Check if this result has been mutated recently
pub fn is_fresh(&self) -> bool {
/// Set this result's value
pub fn set_value(&mut self, new_value: QueryState<T, E>) {
self.value = new_value;
self.instant = Some(Instant::now());
}

/// Check if this result is stale yet
pub fn is_fresh(&self, stale_time: Duration) -> bool {
if let Some(instant) = self.instant {
instant.elapsed().as_millis() < Duration::from_millis(STALE_TIME).as_millis()
instant.elapsed() < stale_time
} else {
false
}
}

/// Check if this result has queried it's actual value yet
pub(crate) fn has_been_queried(&self) -> bool {
self.has_been_queried
/// Check if this result has loaded yet
pub(crate) fn has_been_loaded(&self) -> bool {
self.has_been_loaded
}

/// Set this result as loading
pub(crate) fn set_to_loading(&mut self) {
self.value.set_loading();
self.instant = Some(Instant::now());
self.has_been_queried = true;
self.has_been_loaded = true;
}
}

impl<T, E> Deref for CachedResult<T, E> {
type Target = QueryResult<T, E>;
type Target = QueryState<T, E>;

fn deref(&self) -> &Self::Target {
&self.value
Expand All @@ -60,7 +65,7 @@ impl<T, E> Default for CachedResult<T, E> {
Self {
value: Default::default(),
instant: None,
has_been_queried: false,
has_been_loaded: false,
}
}
}
41 changes: 21 additions & 20 deletions src/result.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
use std::mem;

/// The result of a query.
pub type QueryResult<T, E> = Result<T, E>;

/// The state of a query.
#[derive(Clone, PartialEq, Debug)]
pub enum QueryResult<T, E> {
/// Contains a successful state
Ok(T),
/// Contains an errored state
Err(E),
pub enum QueryState<T, E> {
/// Contains a successful or errored result
Settled(QueryResult<T, E>),
/// Contains a loading state that may or not have a cached result
Loading(Option<T>),
}

impl<T, E> QueryResult<T, E> {
impl<T, E> QueryState<T, E> {
pub fn is_ok(&self) -> bool {
matches!(self, QueryResult::Ok(..))
matches!(self, QueryState::Settled(Ok(..)))
}

pub fn is_err(&self) -> bool {
matches!(self, QueryResult::Err(..))
matches!(self, QueryState::Settled(Err(..)))
}

pub fn is_loading(&self) -> bool {
matches!(self, QueryResult::Loading(..))
matches!(self, QueryState::Loading(..))
}

pub fn set_loading(&mut self) {
Expand All @@ -32,33 +33,33 @@ impl<T, E> QueryResult<T, E> {
}
}

impl<T, E> Default for QueryResult<T, E> {
impl<T, E> Default for QueryState<T, E> {
fn default() -> Self {
Self::Loading(None)
}
}

impl<T, E> From<QueryResult<T, E>> for Option<T> {
fn from(result: QueryResult<T, E>) -> Self {
impl<T, E> From<QueryState<T, E>> for Option<T> {
fn from(result: QueryState<T, E>) -> Self {
match result {
QueryResult::Ok(v) => Some(v),
QueryResult::Err(_) => None,
QueryResult::Loading(v) => v,
QueryState::Settled(Ok(v)) => Some(v),
QueryState::Settled(Err(_)) => None,
QueryState::Loading(v) => v,
}
}
}

impl<T, E> From<Result<T, E>> for QueryResult<T, E> {
impl<T, E> From<Result<T, E>> for QueryState<T, E> {
fn from(value: Result<T, E>) -> Self {
match value {
Ok(v) => QueryResult::Ok(v),
Err(e) => QueryResult::Err(e),
Ok(v) => QueryState::Settled(Ok(v)),
Err(e) => QueryState::Settled(Err(e)),
}
}
}

impl<T, E> From<T> for QueryResult<T, E> {
impl<T, E> From<T> for QueryState<T, E> {
fn from(value: T) -> Self {
QueryResult::Ok(value)
QueryState::Settled(Ok(value))
}
}
Loading

0 comments on commit 08e319d

Please sign in to comment.