yewstack / yewstack/yew

How to cache rendered child VNode to avoid calling Child::change on Parent::view?

Open
#1,037 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
Rust
Stars
32.8k
Forks
1.5k
Avg merge
5h 34m
Merged PRs (30d)
2

Description

My app's main bottleneck is the cloning and comparing of props (of one specific component that is instantiated 4 times) that are huge structs and don't change frame to frame.
This is being caused by Child::change being called every time Parent is rendered (at 30 fps, for all 4 instantiations).
Original code in Parent::view: <Child foo=self.foo.clone()/>
I decided to cache the rendered child's VNode and render that, instead of passing the props every time. (I refresh the cached node manually only when foo really changes.)
I tried two approaches:
1.:

// ~155% cpu usage but rendering smoothly. also calls Child::change when Parent renders. 
// behavior doesn't differ from non-cached rendering of Child
self.cached_child = html! {
	<Child foo=self.foo.clone()/>
};

2.:

// ~105% cpu usage but stuttering, only refreshing about once every second. doesn't call Child::change when Parent renders
self.cached_child = {
	let mut r = Child::default();
	r.change(child::Props {
		foo: self.foo.clone(),
	});
	r.view()
};

And then both times, in Parent::view I do { self.cached_child.clone() } instead of <Child foo=self.foo.clone()/>:

let dynamic_style = format!(
	"transform-origin: {:.4}% center; transform: translateX({:.4}%) scaleX({:.4});",
	xpos_percent,
	50. - xpos_percent,
	self.zoom
);
html! {
	<div style=dynamic_style>
		{ self.cached_child.clone() }
	</div>
}

So it seems only approach 2 is doing what I want (not calling Child::change every time Parent::view is called, not cloning / comparing its foo prop, which is a huge struct) and thus has lower CPU usage.
BUT approach 2 doesn't work: It stutters visually, only updating like once a second.
By updating I mean: The dynamic_style is different every time Parent::view is called, because xpos_percent advances every frame (like in a music player), but with approach 2, it behaves as if dynamic_style is only being updated like once a second instead of at 30 fps, EVEN THOUGH Parent::view is being called!
How can that be? And what's the recommended way to cache a child VNode to avoid calling Child::change when Parent::view is called?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing Parent::view, Child::change, and VNode handling in the two approaches shown. Reproduce the difference between rendering and cloning the cached VNode, including the dynamic_style update, then document the supported way to avoid repeated prop comparison without causing the reported stutter.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, wasm
Domain
frontend, web-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.