1//! Provides a counter that is stable across client and server side. Useful for generating IDs for
2//! accessibility among other things.
3//!
4//! This works internally by using the context API to store the current value of the counter.
56use sycamore_reactive::{use_context_or_else, use_global_scope, Signal};
78#[derive(Debug, Default, Clone, Copy)]
9struct CounterValue {
10 next: Signal<u32>,
11}
1213/// Get the next counter value. This is stable across client and server side.
14///
15/// The counter is stored in the global scope so that it is shared across the entire app. It is
16/// initialized to 0 and incremented everytime this function is called.
17pub fn use_stable_counter() -> u32 {
18let global_scope = use_global_scope();
19let counter = global_scope.run_in(|| use_context_or_else(CounterValue::default));
2021let next = counter.next.get();
22 counter.next.set(next + 1);
23 next
24}
2526#[cfg(test)]
27mod tests {
28use sycamore_reactive::*;
2930use super::*;
3132#[test]
33fn stable_counter_works() {
34let _ = create_root(|| {
35let counter = use_stable_counter();
36assert_eq!(counter, 0);
3738let counter = use_stable_counter();
39assert_eq!(counter, 1);
4041let _ = create_child_scope(|| {
42let counter = use_stable_counter();
43assert_eq!(counter, 2);
44 });
4546let counter = use_stable_counter();
47assert_eq!(counter, 3);
48 });
49 }
50}