Merge branch 'master' into server-fn

This commit is contained in:
Jon Kelley authored and GitHub committed 2023-05-18 13:18:41 +02:00
commit 6bd3437e3a
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
+54
View File
@@ -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
+57
View File
@@ -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
+107
View File
@@ -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),
]),
}
},
),
)
}