1use crate::{Node, Render};
5use std::ops::{Deref, DerefMut};
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.
14/// A renderable that supports caching for when rendering is expensive but can
15/// generate the same DOM tree.
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>>>,
27 /// Construct a new `Cached<R>` of an inner `R`.
32 /// use bumpalo::Bump;
33 /// use dodrio::{Cached, Node, Render};
35 /// pub struct Counter {
39 /// impl Render for Counter {
40 /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
45 /// # unimplemented!()
49 /// // Create a render-able counter.
50 /// let counter = Counter { count: 0 };
52 /// // And cache its rendering!
53 /// let cached_counter = Cached::new(counter);
56 pub fn new(inner: R) -> Cached<R> {
57 let bump = Bump::new();
58 let cached = RefCell::new(None);
66 /// Invalidate the cached rendering.
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.
74 /// The `Cached<Hello>` component must have its cache invalidated whenever
75 /// the `who` string is changed, or else the cached rendering whill keep
76 /// displaying greetings to old `who`s.
79 /// use bumpalo::Bump;
80 /// use dodrio::{Cached, Node, Render};
82 /// /// A component that renders to "<p>Hello, {who}!</p>"
83 /// pub struct Hello {
87 /// impl Render for Hello {
88 /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
92 /// use dodrio::builder::*;
94 /// .children([text("Hello, "), text(&self.who), text("!")])
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) {
103 /// Cached::invalidate(hello);
107 pub fn invalidate(cached: &mut Self) {
108 *cached.cached.borrow_mut() = None;
111 /// Convert a `Cached<R>` back into a plain `R`.
113 pub fn into_inner(cached: Cached<R>) -> R {
118impl<R> Deref for Cached<R> {
121 fn deref(&self) -> &R {
126impl<R> DerefMut for Cached<R> {
127 fn deref_mut(&mut self) -> &mut R {
132unsafe fn extend_node_lifetime<'a>(node: Node<'a>) -> Node<'static> {
136impl<R: Render> Render for Cached<R> {
137 fn render<'a, 'bump>(&'a self, _: &'bump Bump) -> Node<'bump>
141 let mut cached = self.cached.borrow_mut();
143 if let Some(cached) = cached.as_ref() {
144 // The cached node is actually a self-reference, so it has the `'a`
146 let cached: Node<'a> = cached.clone();
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;
152 // Return the cached rendering!
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;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list