Skip to content

Commit

Permalink
Merge branch 'master' into polled-stream
Browse files Browse the repository at this point in the history
  • Loading branch information
futursolo committed Sep 4, 2022
2 parents f491d6c + e60c07f commit 2a300fe
Show file tree
Hide file tree
Showing 20 changed files with 94 additions and 150 deletions.
1 change: 0 additions & 1 deletion examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions examples/boids/src/simulation.rs
Expand Up @@ -25,7 +25,6 @@ pub struct Props {
pub struct Simulation {
boids: Vec<Boid>,
interval: Interval,
settings: Settings,
generation: usize,
}
impl Component for Simulation {
Expand All @@ -49,7 +48,6 @@ impl Component for Simulation {
Self {
boids,
interval,
settings,
generation,
}
}
Expand All @@ -73,10 +71,10 @@ impl Component for Simulation {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
let props = ctx.props();
let should_reset = self.settings != props.settings || self.generation != props.generation;
self.settings = props.settings.clone();
let should_reset =
old_props.settings != props.settings || self.generation != props.generation;
self.generation = props.generation;
if should_reset {
self.boids.clear();
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/author_card.rs
Expand Up @@ -23,7 +23,7 @@ impl Component for AuthorCard {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.author = Author::generate_from_seed(ctx.props().seed);
true
}
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/post_card.rs
Expand Up @@ -23,7 +23,7 @@ impl Component for PostCard {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.post = PostMeta::generate_from_seed(ctx.props().seed);
true
}
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/author.rs
Expand Up @@ -21,7 +21,7 @@ impl Component for Author {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.author = content::Author::generate_from_seed(ctx.props().seed);
true
}
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/post.rs
Expand Up @@ -23,7 +23,7 @@ impl Component for Post {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.post = content::Post::generate_from_seed(ctx.props().seed);
true
}
Expand Down
1 change: 0 additions & 1 deletion examples/webgl/Cargo.toml
Expand Up @@ -9,7 +9,6 @@ license = "MIT OR Apache-2.0"
js-sys = "0.3"
wasm-bindgen = "0.2"
yew = { path = "../../packages/yew", features = ["csr"] }
gloo = "0.8"

[dependencies.web-sys]
version = "0.3"
Expand Down
100 changes: 45 additions & 55 deletions examples/webgl/src/main.rs
@@ -1,41 +1,23 @@
use gloo::render::{request_animation_frame, AnimationFrame};
use std::cell::RefCell;
use std::rc::Rc;

use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{HtmlCanvasElement, WebGlRenderingContext as GL};
use yew::html::Scope;
use web_sys::{window, HtmlCanvasElement, WebGlRenderingContext as GL, WebGlRenderingContext};
use yew::{html, Component, Context, Html, NodeRef};

pub enum Msg {
Render(f64),
}

// Wrap gl in Rc (Arc for multi-threaded) so it can be injected into the render-loop closure.
pub struct App {
gl: Option<GL>,
node_ref: NodeRef,
_render_loop: Option<AnimationFrame>,
}

impl Component for App {
type Message = Msg;
type Message = ();
type Properties = ();

fn create(_ctx: &Context<Self>) -> Self {
Self {
gl: None,
node_ref: NodeRef::default(),
_render_loop: None,
}
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Render(timestamp) => {
// Render functions are likely to get quite large, so it is good practice to split
// it into it's own function rather than keeping it inline in the update match
// case. This also allows for updating other UI elements that may be rendered in
// the DOM like a framerate counter, or other overlaid textual elements.
self.render_gl(timestamp, ctx.link());
false
}
}
}

Expand All @@ -45,44 +27,41 @@ impl Component for App {
}
}

fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
// Only start the render loop if it's the first render
// There's no loop cancellation taking place, so if multiple renders happen,
// there would be multiple loops running. That doesn't *really* matter here because
// there's no props update and no SSR is taking place, but it is something to keep in
// consideration
if !first_render {
return;
}
// Once rendered, store references for the canvas and GL context. These can be used for
// resizing the rendering area when the window or canvas element are resized, as well as
// for making GL calls.

let canvas = self.node_ref.cast::<HtmlCanvasElement>().unwrap();

let gl: GL = canvas
.get_context("webgl")
.unwrap()
.unwrap()
.dyn_into()
.unwrap();

self.gl = Some(gl);

// In a more complex use-case, there will be additional WebGL initialization that should be
// done here, such as enabling or disabling depth testing, depth functions, face
// culling etc.

if first_render {
// The callback to request animation frame is passed a time value which can be used for
// rendering motion independent of the framerate which may vary.
let handle = {
let link = ctx.link().clone();
request_animation_frame(move |time| link.send_message(Msg::Render(time)))
};

// A reference to the handle must be stored, otherwise it is dropped and the render
// won't occur.
self._render_loop = Some(handle);
}
Self::render_gl(gl);
}
}

impl App {
fn render_gl(&mut self, timestamp: f64, link: &Scope<Self>) {
let gl = self.gl.as_ref().expect("GL Context not initialized!");
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
window()
.unwrap()
.request_animation_frame(f.as_ref().unchecked_ref())
.expect("should register `requestAnimationFrame` OK");
}

fn render_gl(gl: WebGlRenderingContext) {
// This should log only once -- not once per frame

let mut timestamp = 0.0;

let vert_code = include_str!("./basic.vert");
let frag_code = include_str!("./basic.frag");
Expand Down Expand Up @@ -123,13 +102,24 @@ impl App {

gl.draw_arrays(GL::TRIANGLES, 0, 6);

let handle = {
let link = link.clone();
request_animation_frame(move |time| link.send_message(Msg::Render(time)))
};
// Gloo-render's request_animation_frame has this extra closure
// wrapping logic running every frame, unnecessary cost.
// Here constructing the wrapped closure just once.

let cb = Rc::new(RefCell::new(None));

*cb.borrow_mut() = Some(Closure::wrap(Box::new({
let cb = cb.clone();
move || {
// This should repeat every frame
timestamp += 20.0;
gl.uniform1f(time.as_ref(), timestamp as f32);
gl.draw_arrays(GL::TRIANGLES, 0, 6);
App::request_animation_frame(cb.borrow().as_ref().unwrap());
}
}) as Box<dyn FnMut()>));

// A reference to the new handle must be retained for the next render to run.
self._render_loop = Some(handle);
App::request_animation_frame(cb.borrow().as_ref().unwrap());
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/src/function_component.rs
Expand Up @@ -298,7 +298,7 @@ impl FunctionComponent {
}

#[inline]
fn changed(&mut self, _ctx: &::yew::html::Context<Self>) -> ::std::primitive::bool {
fn changed(&mut self, _ctx: &::yew::html::Context<Self>, _old_props: &Self::Properties) -> ::std::primitive::bool {
true
}

Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/context.rs
Expand Up @@ -91,7 +91,7 @@ impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
}
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
let props = ctx.props();
let should_render = if self.children == props.children {
false
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/dom_bundle/bcomp.rs
Expand Up @@ -314,7 +314,7 @@ mod tests {
unimplemented!();
}

fn changed(&mut self, _ctx: &Context<Self>) -> bool {
fn changed(&mut self, _ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
unimplemented!();
}

Expand Down
8 changes: 4 additions & 4 deletions packages/yew/src/html/component/lifecycle.rs
Expand Up @@ -205,8 +205,8 @@ where
};

if self.context.props != props {
self.context.props = props;
self.component.changed(&self.context)
let old_props = std::mem::replace(&mut self.context.props, props);
self.component.changed(&self.context, &*old_props)
} else {
false
}
Expand Down Expand Up @@ -791,7 +791,7 @@ mod tests {
false
}

fn changed(&mut self, _ctx: &Context<Self>) -> bool {
fn changed(&mut self, _ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
false
}

Expand Down Expand Up @@ -851,7 +851,7 @@ mod tests {
msg
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.lifecycle = Rc::clone(&ctx.props().lifecycle);
self.lifecycle.borrow_mut().push("change".into());
false
Expand Down
11 changes: 6 additions & 5 deletions packages/yew/src/html/component/mod.rs
Expand Up @@ -100,7 +100,7 @@ pub trait BaseComponent: Sized + 'static {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool;

/// React to changes of component properties.
fn changed(&mut self, ctx: &Context<Self>) -> bool;
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool;

/// Returns a component layout to be rendered.
fn view(&self, ctx: &Context<Self>) -> HtmlResult;
Expand Down Expand Up @@ -154,7 +154,7 @@ pub trait Component: Sized + 'static {
///
/// By default, this function will return true and thus make the component re-render.
#[allow(unused_variables)]
fn changed(&mut self, ctx: &Context<Self>) -> bool {
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
true
}

Expand Down Expand Up @@ -206,8 +206,8 @@ where
Component::update(self, ctx, msg)
}

fn changed(&mut self, ctx: &Context<Self>) -> bool {
Component::changed(self, ctx)
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
Component::changed(self, ctx, old_props)
}

fn view(&self, ctx: &Context<Self>) -> HtmlResult {
Expand All @@ -228,6 +228,7 @@ where
}

#[cfg(test)]
#[cfg(any(feature = "ssr", feature = "csr"))]
mod tests {
use super::*;

Expand Down Expand Up @@ -258,6 +259,6 @@ mod tests {
prepared_state: None,
};
assert!(Component::update(&mut comp, &ctx, ()));
assert!(Component::changed(&mut comp, &ctx));
assert!(Component::changed(&mut comp, &ctx, &Rc::new(())));
}
}
2 changes: 1 addition & 1 deletion packages/yew/src/tests/layout_tests.rs
Expand Up @@ -22,7 +22,7 @@ impl Component for Comp {
unimplemented!();
}

fn changed(&mut self, _ctx: &Context<Self>) -> bool {
fn changed(&mut self, _ctx: &Context<Self>, _: &Self::Properties) -> bool {
unimplemented!()
}

Expand Down
15 changes: 1 addition & 14 deletions website/docs/advanced-topics/optimizations.mdx
Expand Up @@ -82,25 +82,12 @@ implementation of the main page and render the component you are working on on t
## Reducing binary sizes

- optimize Rust code
- `wee_alloc` \( using tiny allocator \)
- `cargo.toml` \( defining release profile \)
- `cargo.toml` \( defining release profile \)
- optimize wasm code using `wasm-opt`

**Note: more information about reducing binary sizes can be found in the
[Rust Wasm Book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size).**

### wee_alloc

[wee_alloc](https://github.com/rustwasm/wee_alloc) is a tiny allocator that is much smaller than the allocator that is normally used in Rust binaries. Replacing the default allocator with this one will result in smaller Wasm file sizes, at the expense of speed and memory overhead.

The slower speed and memory overhead are minor in comparison to the size gains made by not including the default allocator. This smaller file size means that your page will load faster, and so it is generally recommended that you use this allocator over the default, unless your app is doing some allocation-heavy work.

```rust ,ignore
// Use `wee_alloc` as the global allocator.
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
```

### Cargo.toml

It is possible to configure release builds to be smaller using the available settings in the
Expand Down
24 changes: 24 additions & 0 deletions website/docs/migration-guides/yew/from-0_19_0-to-0_20_0.mdx
Expand Up @@ -58,3 +58,27 @@ will result in a compile error
Previously node ref passed to a component was bound to the first element rendered by it.
If this behavior is still desired, it is recommended to use add a `r#ref` field to the
component's properties and bind it manually

## `changed` Method on Components

The method `fn changed()` has now a new argument to provide the old properties
to the function.

The old method's signature was:

```rust ,ignore
fn changed(&mut self, ctx: &Context<Self>) -> bool
```

The new method's signature is now:

```rust ,ignore
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool
```

This can be adjusted automatically in your code using this bash script (save
your code before running this!):

```bash
perl -p -i -e 's/fn changed\(&mut self, (\w+): &Context<Self>\)/fn changed(&mut self, $1: &Context<Self>, _old_props: &Self::Properties)/g' $(find . -name \*.rs)
```

0 comments on commit 2a300fe

Please sign in to comment.