feat: props memoization is more powerful

This commit solves the memoization , properly memoizing properties that don't have any generic parameters. This is a rough heuristic to prevent non-static lifetimes from creeping into props and breaking our minual lifetime management.

Props that have a generic parameter are opted-out of the `partialeq` requirement and props *without* lifetimes must implement partialeq. We're going to leave manual disabling of memoization for future work.
This commit is contained in:
Jonathan Kelley committed 2021-06-22 17:20:54 -04:00
1 parent 7102fe5f98
commit 73047fe956
37 files changed
+877 -977

No files matched your search

+10 -9
View File
@@ -1,5 +1,6 @@
//! Basic example that renders a simple VNode to the browser.
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::*;
@@ -50,15 +51,15 @@ static DocExamples: FC<()> = |ctx| {
}
};
rsx! {
div {}
h1 {}
{""}
"asbasd"
dioxus::Fragment {
//
}
}
// rsx! {
// div {}
// h1 {}
// {""}
// "asbasd"
// dioxus::Fragment {
// //
// }
// }
ctx.render(rsx! {
div {
+8 -10
View File
@@ -2,14 +2,14 @@
//! --------------------
//! This example demonstrates how to use the raw context api for sharing state throughout the VirtualDOM Tree.
//! A custom context must be its own unique type - otherwise use_context will fail. A context may be c
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
@@ -20,7 +20,6 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
}
#[derive(Debug)]
struct CustomContext([&'static str; 3]);
@@ -46,7 +45,6 @@ static Example: FC<()> = |ctx| {
})
};
#[derive(Props, PartialEq)]
struct ButtonProps {
id: u8,
+1
View File
@@ -1,5 +1,6 @@
use std::rc::Rc;
use dioxus_core as dioxus;
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
fn main() {
+1
View File
@@ -1,3 +1,4 @@
use dioxus_core as dioxus;
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
fn main() {
-4
View File
@@ -8,10 +8,6 @@ fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
// Use `wee_alloc` as the global allocator.
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
fn App(ctx: Context<()>) -> VNode {
let cansee = use_state_new(&ctx, || false);
rsx! { in ctx,
+11 -11
View File
@@ -5,17 +5,17 @@ use dioxus_core::prelude::*;
fn main() {}
fn autocomplete() {
let handler = move |evt| {
let r = evt.alt_key();
if evt.alt_key() {}
};
// let handler = move |evt| {
// let r = evt.alt_key();
// if evt.alt_key() {}
// };
let g = rsx! {
button {
button {
onclick: {handler}
}
}
// let g = rsx! {
// button {
// button {
// onclick: {handler}
// }
// }
};
// };
}
@@ -0,0 +1,184 @@
//! JS Framework Benchmark
//! ----------------------
//!
//! This example is used in the JS framework benchmarking tool to compare Dioxus' performance with other frontend frameworks.
//!
//!
//!
use std::rc::Rc;
use dioxus::events::on::MouseEvent;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
// We use a special immutable hashmap to make hashmap operations efficient
type RowList = im_rc::HashMap<usize, Rc<str>, nohash_hasher::BuildNoHashHasher<usize>>;
static App: FC<()> = |cx| {
let (items, set_items) = use_state(&cx, || RowList::default());
let (selection, set_selection) = use_state(&cx, || None as Option<usize>);
let create_rendered_rows = move |from, num| move |_| set_items(create_row_list(from, num));
let append_1_000_rows =
move |_| set_items(create_row_list(items.len(), 1000).union(items.clone()));
let update_every_10th_row = move |_| {
let mut new_items = items.clone();
let mut small_rng = SmallRng::from_entropy();
new_items
.iter_mut()
.step_by(10)
.for_each(|(_, val)| *val = create_new_row_label(&mut small_rng));
set_items(new_items);
};
let clear_rows = move |_| set_items(RowList::default());
let swap_rows = move |_| {
// this looks a bit ugly because we're using a hashmap instead of a vec
if items.len() > 998 {
let mut new_items = items.clone();
let a = new_items.get(&0).unwrap().clone();
*new_items.get_mut(&0).unwrap() = new_items.get(&998).unwrap().clone();
*new_items.get_mut(&998).unwrap() = a;
set_items(new_items);
}
};
let rows = items.iter().map(|(key, value)| {
rsx!(Row {
key: "{key}",
row_id: *key as usize,
label: value.clone(),
})
});
cx.render(rsx! {
div { class: "container"
div { class: "jumbotron"
div { class: "row"
div { class: "col-md-6", h1 { "Dioxus" } }
div { class: "col-md-6"
div { class: "row"
ActionButton { name: "Create 1,000 rows", id: "run", action: create_rendered_rows(0, 1_000) }
ActionButton { name: "Create 10,000 rows", id: "runlots", action: create_rendered_rows(0, 10_000) }
ActionButton { name: "Append 1,000 rows", id: "add", action: append_1_000_rows }
ActionButton { name: "Update every 10th row", id: "update", action: update_every_10th_row, }
ActionButton { name: "Clear", id: "clear", action: clear_rows }
ActionButton { name: "Swap rows", id: "swaprows", action: swap_rows }
}
}
}
}
table {
tbody {
{rows}
}
}
span {}
}
})
};
#[derive(Props)]
struct ActionButtonProps<F: Fn(Rc<dyn MouseEvent>)> {
name: &'static str,
id: &'static str,
action: F,
}
fn ActionButton<F: Fn(Rc<dyn MouseEvent>)>(cx: Context<ActionButtonProps<F>>) -> VNode {
cx.render(rsx! {
div { class: "col-sm-6 smallpad"
button {class:"btn btn-primary btn-block", type: "button", id: "{cx.id}", onclick: {&cx.action},
"{cx.name}"
}
}
})
}
#[derive(PartialEq, Props)]
struct RowProps {
row_id: usize,
label: Rc<str>,
}
fn Row<'a>(cx: Context<'a, RowProps>) -> VNode {
cx.render(rsx! {
tr {
td { class:"col-md-1", "{cx.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
a { class: "lbl", "{cx.label}" }
}
td { class: "col-md-1"
a { class: "remove", onclick: move |_| {/* remove */}
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
}
}
td { class: "col-md-6" }
}
})
}
use rand::prelude::*;
fn create_new_row_label(rng: &mut SmallRng) -> Rc<str> {
let mut label = String::new();
label.push_str(ADJECTIVES.choose(rng).unwrap());
label.push(' ');
label.push_str(COLOURS.choose(rng).unwrap());
label.push(' ');
label.push_str(NOUNS.choose(rng).unwrap());
Rc::from(label)
}
fn create_row_list(from: usize, num: usize) -> RowList {
let mut small_rng = SmallRng::from_entropy();
(from..num + from)
.map(|f| (f, create_new_row_label(&mut small_rng)))
.collect::<RowList>()
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
"orange",
];
static NOUNS: &[&str] = &[
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
"pizza", "mouse", "keyboard",
];
+14 -1
View File
@@ -1,3 +1,4 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
@@ -10,12 +11,24 @@ fn main() {
}
static Example: FC<()> = |ctx| {
let nodes = (0..5).map(|f| {
rsx! {
li {"{f}"}
}
});
ctx.render(rsx! {
div {
span {
class: "px-2 py-1 flex w-36 mt-4 items-center text-xs rounded-md font-semibold text-yellow-500 bg-yellow-100"
"DUE DATE : 18 JUN"
"DUE DATE : 189 JUN"
}
p {
"these"
"are"
"text"
"nodes"
}
{nodes}
}
})
};
+1
View File
@@ -1,3 +1,4 @@
use dioxus_core as dioxus;
use dioxus_web::prelude::*;
fn main() {
+4 -5
View File
@@ -1,3 +1,4 @@
use dioxus_core as dioxus;
use dioxus_core::{events::on::MouseEvent, prelude::*};
use dioxus_web::WebsysRenderer;
@@ -14,7 +15,7 @@ fn main() {
static Example: FC<()> = |ctx| {
let (event, set_event) = use_state(&ctx, || None);
let handler = move |evt: MouseEvent| {
let handler = move |evt| {
set_event(Some(evt));
};
@@ -42,17 +43,15 @@ static Example: FC<()> = |ctx| {
})
};
#[derive(Debug, PartialEq, Props)]
struct ExampleProps {
name: String
name: String,
}
static Example2: FC<ExampleProps> = |ctx| {
ctx.render(rsx!{
ctx.render(rsx! {
div {
h1 {"hello {ctx.name}"}
}
})
};
+7 -14
View File
@@ -26,25 +26,22 @@ static App: FC<()> = |ctx| {
id: "username"
type: "text"
value: "{val}"
oninput: move |evet| {
log::debug!("Value is {:#?}", evet);
set_val(evet.value);
}
oninput: move |evet| set_val(evet.value())
}
p { "Val is: {val}" }
}
}
}
})
})
};
static Example: FC<()> = |ctx| {
ctx.render(rsx! {
div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl"
div { class: "container py-5 max-w-md mx-auto"
h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl",
h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl",
"Text Input Example"
}
}
UserInput {}
}
}
@@ -54,19 +51,15 @@ static Example: FC<()> = |ctx| {
static UserInput: FC<()> = |ctx| {
let (val, set_val) = use_state(&ctx, || "asd".to_string());
rsx!{ in ctx,
rsx! { in ctx,
div { class: "mb-4"
input { class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder: "Username"
id: "username"
id: "username"
type: "text"
oninput: move |evet| {
log::debug!("Value is {:#?}", evet);
set_val(evet.value);
}
oninput: move |evet| set_val(evet.value())
}
p { "Val is: {val}" }
}
}
};
+1 -1
View File
@@ -6,7 +6,7 @@ use dioxus_core::prelude::*;
use dioxus_web::*;
fn main() {
// Setup logging
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
// Run the app
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
+1 -1
View File
@@ -53,7 +53,7 @@ static App: FC<()> = |ctx| {
input {
class: "new-todo"
placeholder: "What needs to be done?"
oninput: move |evt| set_draft(evt.value)
oninput: move |evt| set_draft(evt.value())
}
}
+1
View File
@@ -1,3 +1,4 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
+13
View File
@@ -0,0 +1,13 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
#[derive(Props)]
struct MyProps<'a> {
blah: u128,
b: &'a (),
}
fn main() {
// let p = unsafe { MyProps {}.memoize(&MyProps {}) };
// dbg!(p);
}
+16 -15
View File
@@ -1,14 +1,18 @@
#![allow(non_snake_case)]
use dioxus_core as dioxus;
use std::rc::Rc;
use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_core as dioxus;
use dioxus_web::WebsysRenderer;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
console_error_panic_hook::set_once();
wasm_bindgen_futures::spawn_local(async {
let props = ExampleProps { initial_name: "..?"};
let props = ExampleProps {
initial_name: "..?",
};
WebsysRenderer::new_with_props(Example, props)
.run()
.await
@@ -25,17 +29,17 @@ static Example: FC<ExampleProps> = |ctx| {
let name = use_state_new(&ctx, move || ctx.initial_name);
ctx.render(rsx! {
div {
div {
class: "py-12 px-4 text-center w-full max-w-2xl mx-auto"
span {
span {
class: "text-sm font-semibold"
"Dioxus Example: Jack and Jill"
}
h2 {
class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
h2 {
class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
"Hello, {name}"
}
CustomButton { name: "Jack!", handler: move |_| name.set("Jack") }
CustomButton { name: "Jill!", handler: move |_| name.set("Jill") }
CustomButton { name: "Bob!", handler: move |_| name.set("Bob")}
@@ -45,12 +49,10 @@ static Example: FC<ExampleProps> = |ctx| {
})
};
#[derive(Props)]
struct ButtonProps<'src, F: Fn(MouseEvent)> {
struct ButtonProps<'src, F: Fn(Rc<dyn MouseEvent>)> {
name: &'src str,
handler: F
handler: F,
}
fn CustomButton<'a, F: Fn(MouseEvent)>(ctx: Context<'a, ButtonProps<'a, F>>) -> VNode {
@@ -69,13 +71,12 @@ impl<F: Fn(MouseEvent)> PartialEq for ButtonProps<'_, F> {
}
}
#[derive(Props, PartialEq)]
struct PlaceholderProps {
val: &'static str
val: &'static str,
}
fn Placeholder(ctx: Context<PlaceholderProps>) -> VNode {
ctx.render(rsx!{
ctx.render(rsx! {
div {
"child: {ctx.val}"
}
+8 -7
View File
@@ -1,10 +1,11 @@
use dioxus_core as dioxus;
use dioxus_web::{prelude::*, WebsysRenderer};
mod filtertoggles;
mod recoil;
mod state;
mod todoitem;
mod todolist;
// mod filtertoggles;
// mod recoil;
// mod state;
// mod todoitem;
// mod todolist;
static APP_STYLE: &'static str = include_str!("./style.css");
@@ -13,10 +14,10 @@ fn main() {
ctx.render(rsx! {
div {
id: "app"
style { "{APP_STYLE}" }
// style { "{APP_STYLE}" }
// list
todolist::TodoList {}
// todolist::TodoList {}
// footer
footer {
+10 -12
View File
@@ -1,5 +1,6 @@
use std::{collections::HashMap, rc::Rc};
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
@@ -65,7 +66,7 @@ pub fn TodoList(ctx: Context<()>) -> VNode {
class: "new-todo"
placeholder: "What needs to be done?"
value: "{draft}"
oninput: move |evt| set_draft(evt.value)
oninput: move |evt| set_draft(evt.value())
}
}
@@ -78,7 +79,7 @@ pub fn TodoList(ctx: Context<()>) -> VNode {
FilterState::Completed => item.checked,
})
.map(|(id, item)| {
TodoEntry!();
// TodoEntry!();
todo!()
// rsx!(TodoEntry {
// key: "{order}",
@@ -100,17 +101,14 @@ pub struct TodoEntryProps {
item: Rc<TodoItem>,
}
pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> VNode {
// #[inline_props]
pub fn TodoEntry(
ctx: Context,
baller: &impl Fn() -> (),
caller: &impl Fn() -> (),
todo: &Rc<TodoItem>,
) -> VNode {
// pub fn TodoEntry(ctx: Context, todo: &Rc<TodoItem>) -> VNode {
pub fn TodoEntry(ctx: Context<TodoEntryProps>) -> VNode {
let (is_editing, set_is_editing) = use_state(&ctx, || false);
// let todo = &ctx.item;
let contents = "";
let todo = TodoItem {
checked: false,
contents: "asd".to_string(),
id: uuid::Uuid::new_v4(),
};
ctx.render(rsx! (
li {
+1 -1
View File
@@ -9,7 +9,7 @@
//! Here, we show to use Dioxus' Recoil state management solution to simplify app logic
#![allow(non_snake_case)]
use dioxus_web::dioxus::prelude::*;
use recoil::*;
use std::collections::HashMap;
use uuid::Uuid;