Merge branch 'master' into server-fn
This commit is contained in:
78 files changed
+3946
-445
No files matched your search
@@ -0,0 +1,38 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
// ANCHOR: non_clone_state
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct UseState<'a, T> {
|
||||
value: &'a RefCell<T>,
|
||||
update: Arc<dyn Fn()>,
|
||||
}
|
||||
|
||||
fn my_use_state<T: 'static>(cx: &ScopeState, init: impl FnOnce() -> T) -> UseState<T> {
|
||||
// The update function will trigger a re-render in the component cx is attached to
|
||||
let update = cx.schedule_update();
|
||||
// Create the initial state
|
||||
let value = cx.use_hook(|| RefCell::new(init()));
|
||||
|
||||
UseState { value, update }
|
||||
}
|
||||
|
||||
impl<T: Clone> UseState<'_, T> {
|
||||
fn get(&self) -> T {
|
||||
self.value.borrow().clone()
|
||||
}
|
||||
|
||||
fn set(&self, value: T) {
|
||||
// Update the state
|
||||
*self.value.borrow_mut() = value;
|
||||
// Trigger a re-render on the component the state is from
|
||||
(self.update)();
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: non_clone_state
|
||||
@@ -11,3 +11,57 @@ fn use_settings(cx: &ScopeState) -> &UseSharedState<AppSettings> {
|
||||
use_shared_state::<AppSettings>(cx).expect("App settings not provided")
|
||||
}
|
||||
// ANCHOR_END: wrap_context
|
||||
|
||||
// ANCHOR: use_storage
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
/// A persistent storage hook that can be used to store data across application reloads.
|
||||
#[allow(clippy::needless_return)]
|
||||
pub fn use_persistent<T: Serialize + DeserializeOwned + Default + 'static>(
|
||||
cx: &ScopeState,
|
||||
// A unique key for the storage entry
|
||||
key: impl ToString,
|
||||
// A function that returns the initial value if the storage entry is empty
|
||||
init: impl FnOnce() -> T,
|
||||
) -> &UsePersistent<T> {
|
||||
// Use the use_ref hook to create a mutable state for the storage entry
|
||||
let state = use_ref(cx, move || {
|
||||
// This closure will run when the hook is created
|
||||
let key = key.to_string();
|
||||
let value = LocalStorage::get(key.as_str()).ok().unwrap_or_else(init);
|
||||
StorageEntry { key, value }
|
||||
});
|
||||
|
||||
// Wrap the state in a new struct with a custom API
|
||||
// Note: We use use_hook here so that this hook is easier to use in closures in the rsx. Any values with the same lifetime as the ScopeState can be used in the closure without cloning.
|
||||
cx.use_hook(|| UsePersistent {
|
||||
inner: state.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
struct StorageEntry<T> {
|
||||
key: String,
|
||||
value: T,
|
||||
}
|
||||
|
||||
/// Storage that persists across application reloads
|
||||
pub struct UsePersistent<T: 'static> {
|
||||
inner: UseRef<StorageEntry<T>>,
|
||||
}
|
||||
|
||||
impl<T: Serialize + DeserializeOwned + Clone + 'static> UsePersistent<T> {
|
||||
/// Returns a reference to the value
|
||||
pub fn get(&self) -> T {
|
||||
self.inner.read().value.clone()
|
||||
}
|
||||
|
||||
/// Sets the value
|
||||
pub fn set(&self, value: T) {
|
||||
let mut inner = self.inner.write();
|
||||
// Write the new value to local storage
|
||||
LocalStorage::set(inner.key.as_str(), &value);
|
||||
inner.value = value;
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: use_storage
|
||||
@@ -0,0 +1,57 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
// ANCHOR: use_state
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct UseState<T> {
|
||||
value: Rc<RefCell<T>>,
|
||||
update: Arc<dyn Fn()>,
|
||||
}
|
||||
|
||||
fn my_use_state<T: 'static>(cx: &ScopeState, init: impl FnOnce() -> T) -> &UseState<T> {
|
||||
cx.use_hook(|| {
|
||||
// The update function will trigger a re-render in the component cx is attached to
|
||||
let update = cx.schedule_update();
|
||||
// Create the initial state
|
||||
let value = Rc::new(RefCell::new(init()));
|
||||
|
||||
UseState { value, update }
|
||||
})
|
||||
}
|
||||
|
||||
impl<T: Clone> UseState<T> {
|
||||
fn get(&self) -> T {
|
||||
self.value.borrow().clone()
|
||||
}
|
||||
|
||||
fn set(&self, value: T) {
|
||||
// Update the state
|
||||
*self.value.borrow_mut() = value;
|
||||
// Trigger a re-render on the component the state is from
|
||||
(self.update)();
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: use_state
|
||||
|
||||
// ANCHOR: use_context
|
||||
pub fn use_context<T: 'static + Clone>(cx: &ScopeState) -> Option<&T> {
|
||||
cx.use_hook(|| cx.consume_context::<T>()).as_ref()
|
||||
}
|
||||
|
||||
pub fn use_context_provider<T: 'static + Clone>(cx: &ScopeState, f: impl FnOnce() -> T) -> &T {
|
||||
cx.use_hook(|| {
|
||||
let val = f();
|
||||
// Provide the context state to the scope
|
||||
cx.provide_context(val.clone());
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
// ANCHOR_END: use_context
|
||||
@@ -0,0 +1,107 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut count = use_state(cx, || 0);
|
||||
|
||||
cx.render(
|
||||
// rsx expands to LazyNodes::new
|
||||
::dioxus::core::LazyNodes::new(
|
||||
move |__cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
|
||||
// The template is every static part of the rsx
|
||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
|
||||
// This is the source location of the rsx that generated this template. This is used to make hot rsx reloading work. Hot rsx reloading just replaces the template with a new one generated from the rsx by the CLI.
|
||||
name: "examples\\readme.rs:14:15:250",
|
||||
// The root nodes are the top level nodes of the rsx
|
||||
roots: &[
|
||||
// The h1 node
|
||||
::dioxus::core::TemplateNode::Element {
|
||||
// Find the built in h1 tag in the dioxus_elements crate exported by the dioxus html crate
|
||||
tag: dioxus_elements::h1::TAG_NAME,
|
||||
namespace: dioxus_elements::h1::NAME_SPACE,
|
||||
attrs: &[],
|
||||
// The children of the h1 node
|
||||
children: &[
|
||||
// The dynamic count text node
|
||||
// Any nodes that are dynamic have a dynamic placeholder with a unique index
|
||||
::dioxus::core::TemplateNode::DynamicText {
|
||||
// This index is used to find what element in `dynamic_nodes` to use instead of the placeholder
|
||||
id: 0usize,
|
||||
},
|
||||
],
|
||||
},
|
||||
// The up high button node
|
||||
::dioxus::core::TemplateNode::Element {
|
||||
tag: dioxus_elements::button::TAG_NAME,
|
||||
namespace: dioxus_elements::button::NAME_SPACE,
|
||||
attrs: &[
|
||||
// The dynamic onclick listener attribute
|
||||
// Any attributes that are dynamic have a dynamic placeholder with a unique index.
|
||||
::dioxus::core::TemplateAttribute::Dynamic {
|
||||
// Similar to dynamic nodes, dynamic attributes have a unique index used to find the attribute in `dynamic_attrs` to use instead of the placeholder
|
||||
id: 0usize,
|
||||
},
|
||||
],
|
||||
children: &[::dioxus::core::TemplateNode::Text { text: "Up high!" }],
|
||||
},
|
||||
// The down low button node
|
||||
::dioxus::core::TemplateNode::Element {
|
||||
tag: dioxus_elements::button::TAG_NAME,
|
||||
namespace: dioxus_elements::button::NAME_SPACE,
|
||||
attrs: &[
|
||||
// The dynamic onclick listener attribute
|
||||
::dioxus::core::TemplateAttribute::Dynamic { id: 1usize },
|
||||
],
|
||||
children: &[::dioxus::core::TemplateNode::Text { text: "Down low!" }],
|
||||
},
|
||||
],
|
||||
// Node paths is a list of paths to every dynamic node in the rsx
|
||||
node_paths: &[
|
||||
// The first node path is the path to the dynamic node with an id of 0 (the count text node)
|
||||
&[
|
||||
// Go to the index 0 root node
|
||||
0u8,
|
||||
//
|
||||
// Go to the first child of the root node
|
||||
0u8,
|
||||
],
|
||||
],
|
||||
// Attr paths is a list of paths to every dynamic attribute in the rsx
|
||||
attr_paths: &[
|
||||
// The first attr path is the path to the dynamic attribute with an id of 0 (the up high button onclick listener)
|
||||
&[
|
||||
// Go to the index 1 root node
|
||||
1u8,
|
||||
],
|
||||
// The second attr path is the path to the dynamic attribute with an id of 1 (the down low button onclick listener)
|
||||
&[
|
||||
// Go to the index 2 root node
|
||||
2u8,
|
||||
],
|
||||
],
|
||||
};
|
||||
// The VNode is a reference to the template with the dynamic parts of the rsx
|
||||
::dioxus::core::VNode {
|
||||
parent: None,
|
||||
key: None,
|
||||
// The static template this node will use. The template is stored in a Cell so it can be replaced with a new template when hot rsx reloading is enabled
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: __cx.bump().alloc([
|
||||
// The dynamic count text node (dynamic node id 0)
|
||||
__cx.text_node(format_args!("High-Five counter: {0}", count)),
|
||||
]),
|
||||
dynamic_attrs: __cx.bump().alloc([
|
||||
// The dynamic up high button onclick listener (dynamic attribute id 0)
|
||||
dioxus_elements::events::onclick(__cx, move |_| count += 1),
|
||||
// The dynamic down low button onclick listener (dynamic attribute id 1)
|
||||
dioxus_elements::events::onclick(__cx, move |_| count -= 1),
|
||||
]),
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user