1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
//! Provides `react-query`/`tanstack-query` style hooks for `sycamore`.
//! I aim to eventually have mostly feature parity, but the project is currently
//! in an MVP (minimum viable product) state. This means the basic functionality
//! works (caching, background fetching, invalidations, mutations, refetching),
//! but most of the configurability and automatic refetching on window events
//! is missing. If you need a specific feature or configuration option, feel
//! free to open an issue or even a PR and I'll know to prioritise it.
//!
//! # Usage
//!
//! To use the library you need to provide it with a [`QueryClient`] as a context.
//! This is ideally done in your top level component or index view so your cache
//! is global. If you want to have separate caches for different parts of your
//! app it could make sense to set multiple [`QueryClient`]s.
//!
//! ```
//! # use sycamore::prelude::*;
//! use sycamore_query::{QueryClient, ClientOptions};
//!
//! #[component]
//! pub fn App<G: Html>(cx: Scope) -> View<G> {
//! provide_context(cx, QueryClient::new(ClientOptions::default()));
//!
//! view! { cx, }
//! }
//! ```
//!
//! Now you can use [`use_query`](crate::query::use_query) and
//! [`use_mutation`](crate::mutation::use_mutation) from any of your components.
//!
//! ```
//! # use sycamore::prelude::*;
//! # use sycamore_query::{QueryClient, ClientOptions};
//! use sycamore_query::prelude::*;
//!
//! # mod api {
//! # use std::rc::Rc;
//! # pub async fn hello(name: Rc<String>) -> Result<String, String> {
//! # Ok(name.to_string())
//! # }
//! # }
//!
//! #[component]
//! pub fn Hello<G: Html>(cx: Scope) -> View<G> {
//! # provide_context(cx, QueryClient::new(ClientOptions::default()));
//! let name = create_rc_signal("World".to_string());
//! let Query { data, status, refetch } = use_query(
//! cx,
//! ("hello", name.get()),
//! move || api::hello(name.get())
//! );
//!
//! match data.get_data() {
//! QueryData::Loading => view! { cx, p { "Loading..." } },
//! QueryData::Ok(message) => view! { cx, p { (message) } },
//! QueryData::Err(err) => view! { cx, p { "An error has occured: " } p { (err) } }
//! }
//! }
//! ```
//!
//! This will fetch the data in the background and handle all sorts of things
//! for you: retrying on error (up to 3 times by default), caching, updating when
//! a mutation invalidates the query or another query with the same key fetches
//! the data, etc.
//!
//! # More information
//!
//! I don't have the time to write an entire book on this library right now, so just
//! check out the `react-query` docs and the type level docs for Rust-specific
//! details, keeping in mind only a subset of `react-query` is currently implemented.
use ;
use FnvHasher;
use ;
/// Mutation related functions and types
/// Query related functions and types
/// The sycamore-query prelude.
///
/// In most cases, it is idiomatic to use a glob import (aka wildcard import) at the beginning of
/// your Rust source file.
///
/// ```rust
/// use sycamore_query::prelude::*;
/// ```
pub use *;
pub type Fetcher =
;
pub type DataSignal = ;
/// Trait for anything that can be turned into a key
/// The reason this exists is to allow for prefix invalidation, so lists or
/// tuples should return one hash per element.
/// It's automatically implemented for `String`, `str` and any tuple of size
/// 2 - 12 where each element implements `Hash`.
/// If your keys aren't covered by the default implementation for some reason,
/// you can implement this manually.
///
/// # Example
/// ```
/// # use sycamore_query::AsKey;
/// # use fnv::FnvHasher;
/// # use std::hash::{Hasher, Hash};
/// struct MyType {
/// item1: String,
/// item2: String,
/// }
///
/// impl AsKey for MyType {
/// fn as_key(&self) -> Vec<u64> {
/// let mut hash = FnvHasher::default();
/// self.item1.hash(&mut hash);
/// let hash1 = hash.finish();
/// hash = FnvHasher::default();
/// self.item2.hash(&mut hash);
/// let hash2 = hash.finish();
/// vec![hash1, hash2]
/// }
/// }
/// ```
/// }
// Implement for tuples up to 12 long
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
impl_as_key_tuple!;
/// The data type of a query.
///
/// # States
///
/// * `Loading` - No query data is available yet
/// * `Ok` - Query data was successfully fetched and is available. Note this
/// might be stale data, check `QueryStatus` if you need to verify whether the
/// query is currently fetching fresh data.
/// * `Err` - Query data still wasn't able to be fetched after the retry strategy
/// was exhausted. This contains the backing error.
///
/// The status of a query.
///
/// # States
///
/// * `Fetching` - Query data is currently being fetched. This might be because
/// no data is available ([`QueryData::Loading`]) or because the data is
/// considered stale.
/// * `Success` - Query data is available and fresh.
/// * `Idle` - Query is disabled from running.
/// A convenience macro for passing a set of keys.
/// Keys don't have the same type, so regular `Vec`s don't work.
///
/// # Example Usage
///
/// ```
/// # use sycamore_query::keys;
/// # use std::rc::Rc;
/// # let client = sycamore_query::QueryClient::new(Default::default());
/// client.invalidate_queries(keys![("hello", "World"), "test", ("user", 3)]);
/// ```
///
};
}
/// Utility functions for dealing with QueryData in signals.
;
pub
/// Internal type for tracking key changes. Only exposed because it's used in a public trait
;
/// Internal type for tracking key changes. Only exposed because it's used in a public trait
;
/// Extension to allow for tracking key changes. If I can get some changes into sycamore this should
/// become redundant
///
/// # Usage
///
/// ```
/// # use sycamore::prelude::*;
/// use sycamore_query::prelude::*;
/// # #[component]
/// # pub fn App<G: Html>(cx: Scope) -> View<G> {
/// # async fn hello(s: String) -> Result<String, String> {
/// # Ok(s.to_string())
/// # }
/// let signal = create_signal(cx, "Test");
/// // Updates every time signal changes
/// use_query(cx, ("hello", signal.key()), move || hello(signal.get().to_string());
/// # }
/// ```
/// Extension to allow for tracking key changes. If I can get some changes into sycamore this should
/// become redundant
///
/// # Usage
///
/// ```
/// # use sycamore::prelude::*;
/// use sycamore_query::prelude::*;
/// # #[component]
/// # pub fn App<G: Html>(cx: Scope) -> View<G> {
/// # async fn hello(s: String) -> Result<String, String> {
/// # Ok(s.to_string())
/// # }
/// let signal = create_rc_signal("Test");
/// // Updates every time signal changes
/// use_query(cx, ("hello", signal.clone().rc_key()), move || hello(signal.get().to_string());
/// # }
/// ```