Skip to content

Commit

Permalink
Create memo view
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Jan 26, 2024
1 parent 1399b20 commit 957cec7
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<h1>Concoct</h1>

<a href="https://crates.io/crates/concoct">
<img src="https://img.shields.io/crates/v/concoct?style=flat-square"
alt="Crates.io version" />
Expand All @@ -23,10 +23,9 @@

Concoct is a framework for user-interfaces in Rust.

This crate provides a diffing-engine and state management system for any backend.
This crate provides a virtual DOM and state management system for any backend.
Concoct uses static typing to describe your UI at compile-time to create an efficient
tree without allocations. Updates to state re-render your application top-down,
starting at the state's parent component.
tree without allocations.

```rust
#[derive(Default)]
Expand Down
25 changes: 24 additions & 1 deletion crates/concoct/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
//! Concoct is a framework for user-interfaces in Rust.
//!
//! This crate provides a virtual DOM and state management system for any backend.
//! Concoct uses static typing to describe your UI at compile-time to create an efficient
//! tree without allocations.
//!
//! ```ignore
//! #[derive(Default)]
//! struct Counter {
//! count: i32,
//! }
//!
//! impl View<Counter> for Counter {
//! fn body(&mut self, _cx: &Scope<Counter>) -> impl View<Counter> {
//! (
//! format!("High five count: {}", self.count),
//! html::button("Up high!").on_click(|state: &mut Self, _event| state.count += 1),
//! html::button("Down low!").on_click(|state: &mut Self, _event| state.count -= 1),
//! )
//! }
//! }
//! ```

use rustc_hash::FxHashMap;
use slotmap::{DefaultKey, SlotMap};
use std::{
Expand Down Expand Up @@ -29,7 +52,7 @@ impl<T, A> Handle<T, A> {
}

pub struct Scope<T, A = ()> {
pub key: DefaultKey,
key: DefaultKey,
node: Node,
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<ActionResult<A>>>)>,
is_empty: Cell<bool>,
Expand Down
11 changes: 9 additions & 2 deletions crates/concoct/src/view/adapt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{build_inner, ActionResult, Scope, View};
use crate::{build_inner, rebuild_inner, ActionResult, Scope, View};
use std::{cell::RefCell, marker::PhantomData, rc::Rc};

/// Adapt a view's state to a different one.
pub fn adapt<T1, A1, F, V, T2, A2>(f: F, view: V) -> Adapt<T1, A1, F, V, T2, A2>
where
T1: 'static,
Expand All @@ -18,6 +19,7 @@ where
}
}

/// View for the [`adapt`] function.
pub struct Adapt<T1, A1, F, V, T2, A2> {
f: Rc<RefCell<F>>,
view: V,
Expand Down Expand Up @@ -49,6 +51,11 @@ where
nodes: cx.nodes.clone(),
contexts: cx.contexts.clone(),
};
build_inner(&mut self.view, &child_cx);

if cx.node.inner.borrow().children.is_empty() {
build_inner(&mut self.view, &child_cx);
} else {
rebuild_inner(&mut self.view, &child_cx);
}
}
}
41 changes: 41 additions & 0 deletions crates/concoct/src/view/memo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::{build_inner, hook::use_ref, rebuild_inner, Scope, View};
use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};

/// Memoize a view, only running it when dependencies have change.
pub fn memo<V>(dependencies: impl Hash, view: V) -> Memo<V> {
let mut hasher = FxHasher::default();
dependencies.hash(&mut hasher);
let hash = hasher.finish();

Memo { hash, view }
}

/// View for the [`memo`] function.
pub struct Memo<V> {
hash: u64,
view: V,
}

impl<T, A, V> View<T, A> for Memo<V>
where
V: View<T, A>,
{
fn body(&mut self, cx: &Scope<T, A>) -> impl View<T, A> {
let mut is_init = false;
let last_hash = use_ref(cx, || {
is_init = true;
self.hash
});

if is_init || self.hash != *last_hash {
*last_hash = self.hash;

if cx.node.inner.borrow().children.is_empty() {
build_inner(&mut self.view, &cx);
} else {
rebuild_inner(&mut self.view, &cx);
}
}
}
}
5 changes: 5 additions & 0 deletions crates/concoct/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ use std::{cell::Cell, rc::Rc};
mod adapt;
pub use self::adapt::{adapt, Adapt};

mod memo;
pub use self::memo::{memo, Memo};

/// Viewable component.
pub trait View<T, A = ()> {
/// View this component, returning its body.
fn body(&mut self, cx: &Scope<T, A>) -> impl View<T, A>;
}

Expand Down

0 comments on commit 957cec7

Please sign in to comment.