on master help sign in
Temporarily remove `Cached<T>`
34979f8Nick Fitzgerald
src/cached.rsdeleted−163
@@ -1,163 +0,0 @@
1−use crate::{Node, Render};
2−use bumpalo::Bump;
3−use std::cell::RefCell;
4−use std::mem;
5−use std::ops::{Deref, DerefMut};
6−
7−// TODO: This implementation will only cache the rendering (generation of the
8−// virtual DOM) but not the diffing of the cached subtree. We could skip diffing
9−// for cached tree by adding `fn is_cached(&self) -> bool` to `Node` that we can
10−// check during diffing. This comes at the cost of bloating `Node`'s size (since
11−// we don't have any padding to sneak an extra `bool` field into). We should
12−// investigate whether it is worth adding this or not.
13−
14−/// A renderable that supports caching for when rendering is expensive but can
15−/// generate the same DOM tree.
16−#[derive(Debug)]
17−pub struct Cached<R> {
18− inner: R,
19− bump: bumpalo::Bump,
20− // Actually a self-reference into `self.bump`. Safe because we ensure that
21− // whenever we hand out a cached node, we use a lifetime that cannot outlive
22− // its owning `Cached<R>`.
23− cached: RefCell<Option<Node<'static>>>,
24−}
25−
26−impl<R> Cached<R> {
27− /// Construct a new `Cached<R>` of an inner `R`.
28− ///
29− /// # Example
30− ///
31− /// ```
32− /// use bumpalo::Bump;
33− /// use dodrio::{Cached, Node, Render};
34− ///
35− /// pub struct Counter {
36− /// count: u32,
37− /// }
38− ///
39− /// impl Render for Counter {
40− /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
41− /// where
42− /// 'a: 'bump
43− /// {
44− /// // ...
45− /// # unimplemented!()
46− /// }
47− /// }
48− ///
49− /// // Create a render-able counter.
50− /// let counter = Counter { count: 0 };
51− ///
52− /// // And cache its rendering!
53− /// let cached_counter = Cached::new(counter);
54− /// ```
55− #[inline]
56− pub fn new(inner: R) -> Cached<R> {
57− let bump = Bump::new();
58− let cached = RefCell::new(None);
59− Cached {
60− inner,
61− bump,
62− cached,
63− }
64− }
65−
66− /// Invalidate the cached rendering.
67− ///
68− /// This method should be called whenever the inner `R` must be re-rendered,
69− /// and the cached `Node` from the last time `R::render` was invoked can no
70− /// longer be re-used.
71− ///
72− /// # Example
73− ///
74− /// The `Cached<Hello>` component must have its cache invalidated whenever
75− /// the `who` string is changed, or else the cached rendering will keep
76− /// displaying greetings to old `who`s.
77− ///
78− /// ```
79− /// use bumpalo::Bump;
80− /// use dodrio::{Cached, Node, Render};
81− ///
82− /// /// A component that renders to "<p>Hello, {who}!</p>"
83− /// pub struct Hello {
84− /// who: String
85− /// }
86− ///
87− /// impl Render for Hello {
88− /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
89− /// where
90− /// 'a: 'bump,
91− /// {
92− /// use dodrio::builder::*;
93− /// p(bump)
94− /// .children([text("Hello, "), text(&self.who), text("!")])
95− /// .finish()
96− /// }
97− /// }
98− ///
99− /// /// Whenever a `Cached<Hello>`'s `who` is updated, we need to invalidate the
100− /// /// cache so that we don't keep displaying greetings to old `who`s.
101− /// pub fn set_who(hello: &mut Cached<Hello>, who: String) {
102− /// hello.who = who;
103− /// Cached::invalidate(hello);
104− /// }
105− /// ```
106− #[inline]
107− pub fn invalidate(cached: &mut Self) {
108− *cached.cached.borrow_mut() = None;
109− }
110−
111− /// Convert a `Cached<R>` back into a plain `R`.
112− #[inline]
113− pub fn into_inner(cached: Cached<R>) -> R {
114− cached.inner
115− }
116−}
117−
118−impl<R> Deref for Cached<R> {
119− type Target = R;
120−
121− fn deref(&self) -> &R {
122− &self.inner
123− }
124−}
125−
126−impl<R> DerefMut for Cached<R> {
127− fn deref_mut(&mut self) -> &mut R {
128− &mut self.inner
129− }
130−}
131−
132−unsafe fn extend_node_lifetime<'a>(node: Node<'a>) -> Node<'static> {
133− mem::transmute(node)
134−}
135−
136−impl<R: Render> Render for Cached<R> {
137− fn render<'a, 'bump>(&'a self, _: &'bump Bump) -> Node<'bump>
138− where
139− 'a: 'bump,
140− {
141− let mut cached = self.cached.borrow_mut();
142−
143− if let Some(cached) = cached.as_ref() {
144− // The cached node is actually a self-reference, so it has the `'a`
145− // lifetime.
146− let cached: Node<'a> = cached.clone();
147−
148− // But the `'a` lifetime outlives `'bump`, so we can safely convert
149− // it to the narrower `'bump` lifetime.
150− let cached: Node<'bump> = cached;
151−
152− // Return the cached rendering!
153− return cached;
154− }
155−
156− // We don't have a cached node. Render into our own `Bump`, cache it for
157− // future renders, and return it. Same lifetimes as above.
158− let node: Node<'a> = self.inner.render(&self.bump);
159− *cached = Some(unsafe { extend_node_lifetime(node.clone()) });
160− let node: Node<'bump> = node;
161− node
162− }
163−}
src/lib.rs−2
@@ -67,7 +67,6 @@ cfg_if::cfg_if! {
67#[doc(hidden)]
67#[doc(hidden)]
68pub mod change_list;
68pub mod change_list;
69
69
70−mod cached;
71mod events;
70mod events;
72mod node;
71mod node;
73mod render;
72mod render;
@@ -76,7 +75,6 @@ mod vdom;
76pub mod builder;
75pub mod builder;
77
76
78// Re-export items at the top level.
77// Re-export items at the top level.
79−pub use self::cached::Cached;
80pub use self::node::{Attribute, ElementNode, Listener, ListenerCallback, Node, TextNode};
78pub use self::node::{Attribute, ElementNode, Listener, ListenerCallback, Node, TextNode};
81pub use self::render::{Render, RootRender};
79pub use self::render::{Render, RootRender};
82pub use self::vdom::{Vdom, VdomWeak};
80pub use self::vdom::{Vdom, VdomWeak};
tests/web/cached.rsdeleted−101
@@ -1,101 +0,0 @@
1−use super::{assert_rendered, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo::Bump, Cached, Node, Render, Vdom};
3−use futures::prelude::*;
4−use std::cell::Cell;
5−use std::rc::Rc;
6−use wasm_bindgen::*;
7−use wasm_bindgen_test::*;
8−
9−pub struct CountRenders {
10− render_count: Cell<usize>,
11−}
12−
13−impl CountRenders {
14− pub fn new() -> CountRenders {
15− CountRenders {
16− render_count: Cell::new(0),
17− }
18− }
19−}
20−
21−impl Render for CountRenders {
22− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
23− where
24− 'a: 'bump,
25− {
26− let count = self.render_count.get() + 1;
27− self.render_count.set(count);
28−
29− let s = bumpalo::format!(in bump, "{}", count);
30− text(s.into_bump_str())
31− }
32−}
33−
34−#[wasm_bindgen_test(async)]
35−fn uses_cached_render() -> impl Future<Item = (), Error = JsValue> {
36− use dodrio::builder::*;
37−
38− let cached = Cached::new(CountRenders::new());
39−
40− let container0 = create_element("div");
41− let container1 = container0.clone();
42− let container2 = container0.clone();
43− let container3 = container0.clone();
44− let container4 = container0.clone();
45−
46− let vdom0 = Rc::new(Vdom::new(&container0, cached));
47− let vdom1 = vdom0.clone();
48− let vdom2 = vdom0.clone();
49− let vdom3 = vdom0.clone();
50− let vdom4 = vdom0.clone();
51− let vdom5 = vdom0.clone();
52− let vdom6 = vdom0.clone();
53− let vdom7 = vdom0.clone();
54−
55− vdom0
56− .weak()
57− // We render, populate the cache, and get "1".
58− .render()
59− .and_then(move |_| {
60− vdom0.weak().with_component(move |comp| {
61− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
62− assert_eq!(comp.render_count.get(), 1);
63− assert_rendered(&container1, &RenderFn(|_| text("1")));
64− })
65− })
66− // We re-render, re-use the cached node, and get "1" again.
67− .and_then(move |_| vdom1.weak().render())
68− .and_then(move |_| {
69− vdom2.weak().with_component(move |comp| {
70− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
71− assert_eq!(comp.render_count.get(), 1);
72− assert_rendered(&container2, &RenderFn(|_| text("1")));
73− })
74− })
75− // We invalidate the cache, re-render, re-populate the cache, and should
76− // now have "2".
77− .and_then(move |_| {
78− vdom3.weak().with_component(move |comp| {
79− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
80− Cached::invalidate(comp);
81− })
82− })
83− .and_then(move |_| vdom4.weak().render())
84− .and_then(move |_| {
85− vdom5.weak().with_component(move |comp| {
86− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
87− assert_eq!(comp.render_count.get(), 2);
88− assert_rendered(&container3, &RenderFn(|_| text("2")));
89− })
90− })
91− // We re-render, re-use the cached node, and get "2" again.
92− .and_then(move |_| vdom6.weak().render())
93− .and_then(move |_| {
94− vdom7.weak().with_component(move |comp| {
95− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
96− assert_eq!(comp.render_count.get(), 2);
97− assert_rendered(&container4, &RenderFn(|_| text("2")));
98− })
99− })
100− .map_err(|e| JsValue::from(e.to_string()))
101−}
tests/web/events.rs−74
@@ -272,77 +272,3 @@ fn event_does_not_fire_after_unmounting() -> impl Future<Item = (), Error = JsVa
272 })
272 })
273 .map_err(|e| JsValue::from(e.to_string()))
273 .map_err(|e| JsValue::from(e.to_string()))
274}
274}
275−
276−struct CountRendersAndListen {
277− count: Cell<usize>,
278− callback: Box<FnMut(usize)>,
279−}
280−
281−impl CountRendersAndListen {
282− fn new<F>(callback: F) -> CountRendersAndListen
283− where
284− F: 'static + FnMut(usize),
285− {
286− CountRendersAndListen {
287− count: Cell::new(0),
288− callback: Box::new(callback),
289− }
290− }
291−}
292−
293−impl Render for CountRendersAndListen {
294− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
295− where
296− 'a: 'bump,
297− {
298− use dodrio::builder::*;
299−
300− let count = self.count.get() + 1;
301− self.count.set(count);
302−
303− button(bump)
304− .attr("id", "target")
305− .on("click", |comp, _vdom, _event| {
306− let comp: &mut _ = comp.unwrap_mut::<dodrio::Cached<CountRendersAndListen>>();
307− let comp: &mut CountRendersAndListen = comp;
308− let n = comp.count.get();
309− (comp.callback)(n);
310− })
311− .finish()
312− }
313−}
314−
315−#[wasm_bindgen_test(async)]
316−fn cached_listeners_work_correctly() -> impl Future<Item = (), Error = JsValue> {
317− let container = create_element("div");
318−
319− let (sender, receiver) = futures::sync::oneshot::channel();
320− let mut sender = Some(sender);
321− let vdom = Vdom::new(
322− &container,
323− dodrio::Cached::new(CountRendersAndListen::new(move |n| {
324− sender.take().unwrap().send(n).unwrap();
325− })),
326− );
327−
328− let weak0 = vdom.weak();
329− let weak1 = vdom.weak();
330− let weak2 = vdom.weak();
331− vdom.weak()
332− // Re-render a few times, so we are using the cached version.
333− .render()
334− .and_then(move |_| weak0.render())
335− .and_then(move |_| weak1.render())
336− .and_then(move |_| weak2.render())
337− .map_err(|e| JsValue::from(e.to_string()))
338− .and_then(move |_| {
339− target(&container).click();
340−
341− receiver
342− .map(|n| {
343− assert_eq!(n, 1);
344− drop(vdom);
345− })
346− .map_err(|e| JsValue::from(e.to_string()))
347− })
348−}
tests/web/main.rs−1
@@ -13,7 +13,6 @@ use wasm_bindgen_test::*;
13
13
14wasm_bindgen_test_configure!(run_in_browser);
14wasm_bindgen_test_configure!(run_in_browser);
15
15
16−pub mod cached;
17pub mod events;
16pub mod events;
18pub mod js_api;
17pub mod js_api;
19pub mod render;
18pub mod render;