/leptos_query

Asynchronous state management library for Leptos, providing simplified data fetching, integrated reactivity, server-side rendering support, and intelligent cache management.

Primary LanguageRustMIT LicenseMIT

Leptos Query

Leptos Query

Crates.io Crates.io

FAQ | Docs.rs | Examples

About

Leptos Query is a robust asynchronous state management library for Leptos, providing simplified data fetching, integrated reactivity, server-side rendering support, and intelligent cache management.

Heavily inspired by Tanstack Query.

Read the introduction article here: The Forging of Leptos Query

Why Choose Leptos Query?

Leptos Query focuses on simplifying your data fetching process and keeping your application's state effortlessly synchronized and up-to-date. Here's how it's done:

Key Features

  • Configurable Caching & SWR: Queries are cached by default, ensuring quick access to your data. You can configure your stale and cache times per query with Stale While Revalidate (SWR) system.

  • Reactivity at the Core: Leptos Query deeply integrates with Leptos' reactive system to transform asynchronous query fetchers into reactive Signals.

  • Server-Side Rendering (SSR) Compatibility: Fetch your queries on the server and smoothly serialize them to the client, just as you would with a Leptos Resource.

  • Efficient De-duplication: No unnecessary fetches here! If you make multiple queries with the same Key, Leptos Query smartly fetches only once.

  • Manual Invalidation: Control when your queries should be invalidated and refetched for that ultimate flexibility.

  • Scheduled Refetching: Set up your queries to refetch on a customized schedule, keeping your data fresh as per your needs.

  • Manual Query Data Mutations: Useful when you have updated a value and you want to manually set it in cache instead of waiting for query to refetch.

Installation

cargo add leptos_query

Then add the relevant feature(s) to your Cargo.toml

[features]
hydrate = [
    "leptos_query/hydrate",
    # ...
]
ssr = [
    "leptos_query/ssr",
    # ...
 ]

Quick Start

If you are using SSR you may have to use supress_query_load in your server's main function. See the FAQ for more information.

In the root of your App, provide a query client:

use leptos_query::*;
use leptos::*;

#[component]
pub fn App() -> impl IntoView {
    // Provides Query Client for entire app.
    provide_query_client();

    // Rest of App...
}

Then make a query function.

 use leptos::*;
 use leptos_query::*;
 use std::time::Duration;
 use serde::*;

 // Data type.
 #[derive(Clone, Deserialize, Serialize)]
 struct Monkey {
     name: String,
 }


 // Monkey fetcher.
 async fn get_monkey(id: String) -> Monkey {
    todo!()
 }

 // Query for a Monkey.
 fn use_monkey_query(id: impl Fn() -> String + 'static) -> QueryResult<Monkey, impl RefetchFn> {
     leptos_query::use_query(
         id,
         get_monkey,
         QueryOptions {
             default_value: None,
             refetch_interval: None,
             resource_option: ResourceOption::NonBlocking,
             // Considered stale after 10 seconds.
             stale_time: Some(Duration::from_secs(10)),
             // Infinite cache time.
             cache_time: None,
         },
     )
 }

Now you can use the query in any component in your app.

#[component]
fn MonkeyView(id: String) -> impl IntoView {
    let QueryResult {
        data,
        is_loading,
        is_fetching,
        is_stale
        ..
    } = use_monkey_query(move || id.clone());

    view! {
      // You can use the query result data here.
      // Everything is reactive.
       <div>
           <div>
               <span>"Loading Status: "</span>
               <span>{move || { if is_loading.get() { "Loading..." } else { "Loaded" } }}</span>
           </div>
           <div>
               <span>"Fetching Status: "</span>
               <span>
                   {move || { if is_fetching.get() { "Fetching..." } else { "Idle" } }}
               </span>
           </div>
           <div>
               <span>"Stale Status: "</span>
               <span>
                   {move || { if is_stale.get() { "Stale" } else { "Fresh" } }}
               </span>
           </div>
           // Query data should be read inside a Transition/Suspense component.
           <Transition
               fallback=move || {
                   view! { <h2>"Loading..."</h2> }
               }>
               {move || {
                   data.get()
                       .map(|monkey| {
                           view! { <h2>{monkey.name}</h2> }
                       })
               }}
           </Transition>
       </div>
    }
}

For a complete working example see the example directory