mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Remove IntoComponent (#2579)
* Make Function Component to Implement BaseComponent. * Remove IntoComponent. * Inline some methods. * Move some logic out of proc macro. * Move position of the self generics. * Discourage direct implementation `FunctionComponent` & `#[doc(hidden)]`.
This commit is contained in:
parent
6fb547339f
commit
c5b7790949
@ -4,8 +4,8 @@ use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::{Comma, Fn};
|
||||
use syn::{
|
||||
parse_quote_spanned, visit_mut, Attribute, Block, FnArg, Generics, Ident, Item, ItemFn,
|
||||
ReturnType, Type, Visibility,
|
||||
parse_quote, parse_quote_spanned, visit_mut, Attribute, Block, FnArg, Generics, Ident, Item,
|
||||
ItemFn, LitStr, ReturnType, Type, Visibility,
|
||||
};
|
||||
|
||||
use crate::hook::BodyRewriter;
|
||||
@ -219,8 +219,8 @@ impl FunctionComponent {
|
||||
.unwrap_or_else(|| self.name.clone())
|
||||
}
|
||||
|
||||
// We need to cast 'static on all generics for into component.
|
||||
fn create_into_component_generics(&self) -> Generics {
|
||||
// We need to cast 'static on all generics for base component.
|
||||
fn create_static_component_generics(&self) -> Generics {
|
||||
let mut generics = self.generics.clone();
|
||||
|
||||
let where_clause = generics.make_where_clause();
|
||||
@ -233,8 +233,158 @@ impl FunctionComponent {
|
||||
where_clause.predicates.push(bound);
|
||||
}
|
||||
|
||||
where_clause.predicates.push(parse_quote! { Self: 'static });
|
||||
|
||||
generics
|
||||
}
|
||||
|
||||
/// Prints the impl fn.
|
||||
fn print_inner_fn(&self) -> TokenStream {
|
||||
let name = self.inner_fn_ident();
|
||||
let FunctionComponent {
|
||||
ref fn_token,
|
||||
ref attrs,
|
||||
ref block,
|
||||
ref return_type,
|
||||
ref generics,
|
||||
ref arg,
|
||||
..
|
||||
} = self;
|
||||
let mut block = *block.clone();
|
||||
let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
// We use _ctx here so if the component does not use any hooks, the usused_vars lint will not
|
||||
// be triggered.
|
||||
let ctx_ident = Ident::new("_ctx", Span::mixed_site());
|
||||
|
||||
let mut body_rewriter = BodyRewriter::new(ctx_ident.clone());
|
||||
visit_mut::visit_block_mut(&mut body_rewriter, &mut block);
|
||||
|
||||
quote! {
|
||||
#(#attrs)*
|
||||
#fn_token #name #impl_generics (#ctx_ident: &mut ::yew::functional::HookContext, #arg) -> #return_type
|
||||
#where_clause
|
||||
{
|
||||
#block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_base_component_impl(&self) -> TokenStream {
|
||||
let component_name = self.component_name();
|
||||
let props_type = &self.props_type;
|
||||
let static_comp_generics = self.create_static_component_generics();
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = static_comp_generics.split_for_impl();
|
||||
|
||||
// TODO: replace with blanket implementation when specialisation becomes stable.
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::yew::html::BaseComponent for #component_name #ty_generics #where_clause {
|
||||
type Message = ();
|
||||
type Properties = #props_type;
|
||||
|
||||
#[inline]
|
||||
fn create(ctx: &::yew::html::Context<Self>) -> Self {
|
||||
Self {
|
||||
_marker: ::std::marker::PhantomData,
|
||||
function_component: ::yew::functional::FunctionComponent::<Self>::new(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update(&mut self, _ctx: &::yew::html::Context<Self>, _msg: Self::Message) -> ::std::primitive::bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn changed(&mut self, _ctx: &::yew::html::Context<Self>) -> ::std::primitive::bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn view(&self, ctx: &::yew::html::Context<Self>) -> ::yew::html::HtmlResult {
|
||||
::yew::functional::FunctionComponent::<Self>::render(
|
||||
&self.function_component,
|
||||
::yew::html::Context::<Self>::props(ctx)
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rendered(&mut self, _ctx: &::yew::html::Context<Self>, _first_render: ::std::primitive::bool) {
|
||||
::yew::functional::FunctionComponent::<Self>::rendered(&self.function_component)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn destroy(&mut self, _ctx: &::yew::html::Context<Self>) {
|
||||
::yew::functional::FunctionComponent::<Self>::destroy(&self.function_component)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_debug_impl(&self) -> TokenStream {
|
||||
let component_name = self.component_name();
|
||||
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
|
||||
|
||||
let component_name_lit = LitStr::new(&format!("{}<_>", component_name), Span::mixed_site());
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::std::fmt::Debug for #component_name #ty_generics #where_clause {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::std::write!(f, #component_name_lit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_fn_provider_impl(&self) -> TokenStream {
|
||||
let func = self.print_inner_fn();
|
||||
let component_impl_attrs = self.filter_attrs_for_component_impl();
|
||||
let component_name = self.component_name();
|
||||
let fn_name = self.inner_fn_ident();
|
||||
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
|
||||
let props_type = &self.props_type;
|
||||
let fn_generics = ty_generics.as_turbofish();
|
||||
|
||||
let component_props = Ident::new("props", Span::mixed_site());
|
||||
let ctx_ident = Ident::new("ctx", Span::mixed_site());
|
||||
|
||||
quote! {
|
||||
// we cannot disable any lints here because it will be applied to the function body
|
||||
// as well.
|
||||
#(#component_impl_attrs)*
|
||||
impl #impl_generics ::yew::functional::FunctionProvider for #component_name #ty_generics #where_clause {
|
||||
type Properties = #props_type;
|
||||
|
||||
fn run(#ctx_ident: &mut ::yew::functional::HookContext, #component_props: &Self::Properties) -> ::yew::html::HtmlResult {
|
||||
#func
|
||||
|
||||
::yew::html::IntoHtmlResult::into_html_result(#fn_name #fn_generics (#ctx_ident, #component_props))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_struct_def(&self) -> TokenStream {
|
||||
let component_attrs = self.filter_attrs_for_component_struct();
|
||||
let component_name = self.component_name();
|
||||
|
||||
let generics = &self.generics;
|
||||
let (_impl_generics, _ty_generics, where_clause) = self.generics.split_for_impl();
|
||||
let phantom_generics = self.phantom_generics();
|
||||
let vis = &self.vis;
|
||||
|
||||
quote! {
|
||||
#(#component_attrs)*
|
||||
#[allow(unused_parens)]
|
||||
#vis struct #component_name #generics #where_clause {
|
||||
_marker: ::std::marker::PhantomData<(#phantom_generics)>,
|
||||
function_component: ::yew::functional::FunctionComponent<Self>,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionComponentName {
|
||||
@ -257,96 +407,23 @@ impl Parse for FunctionComponentName {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_fn(func_comp: &FunctionComponent) -> TokenStream {
|
||||
let name = func_comp.inner_fn_ident();
|
||||
let FunctionComponent {
|
||||
ref fn_token,
|
||||
ref attrs,
|
||||
ref block,
|
||||
ref return_type,
|
||||
ref generics,
|
||||
ref arg,
|
||||
..
|
||||
} = func_comp;
|
||||
let mut block = *block.clone();
|
||||
let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
// We use _ctx here so if the component does not use any hooks, the usused_vars lint will not
|
||||
// be triggered.
|
||||
let ctx_ident = Ident::new("_ctx", Span::mixed_site());
|
||||
|
||||
let mut body_rewriter = BodyRewriter::new(ctx_ident.clone());
|
||||
visit_mut::visit_block_mut(&mut body_rewriter, &mut block);
|
||||
|
||||
quote! {
|
||||
#(#attrs)*
|
||||
#fn_token #name #impl_generics (#ctx_ident: &mut ::yew::functional::HookContext, #arg) -> #return_type
|
||||
#where_clause
|
||||
{
|
||||
#block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_component_impl(
|
||||
name: FunctionComponentName,
|
||||
mut component: FunctionComponent,
|
||||
) -> syn::Result<TokenStream> {
|
||||
component.merge_component_name(name)?;
|
||||
|
||||
let func = print_fn(&component);
|
||||
|
||||
let into_comp_generics = component.create_into_component_generics();
|
||||
let component_attrs = component.filter_attrs_for_component_struct();
|
||||
let component_impl_attrs = component.filter_attrs_for_component_impl();
|
||||
let phantom_generics = component.phantom_generics();
|
||||
let component_name = component.component_name();
|
||||
let fn_name = component.inner_fn_ident();
|
||||
|
||||
let FunctionComponent {
|
||||
props_type,
|
||||
generics,
|
||||
vis,
|
||||
..
|
||||
} = component;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
let fn_generics = ty_generics.as_turbofish();
|
||||
|
||||
let component_props = Ident::new("props", Span::mixed_site());
|
||||
let ctx_ident = Ident::new("ctx", Span::mixed_site());
|
||||
|
||||
let into_comp_impl = {
|
||||
let (impl_generics, ty_generics, where_clause) = into_comp_generics.split_for_impl();
|
||||
|
||||
quote! {
|
||||
impl #impl_generics ::yew::html::IntoComponent for #component_name #ty_generics #where_clause {
|
||||
type Properties = #props_type;
|
||||
type Component = ::yew::functional::FunctionComponent<Self>;
|
||||
}
|
||||
}
|
||||
};
|
||||
let base_comp_impl = component.print_base_component_impl();
|
||||
let debug_impl = component.print_debug_impl();
|
||||
let provider_fn_impl = component.print_fn_provider_impl();
|
||||
let struct_def = component.print_struct_def();
|
||||
|
||||
let quoted = quote! {
|
||||
#(#component_attrs)*
|
||||
#[allow(unused_parens)]
|
||||
#vis struct #component_name #generics #where_clause {
|
||||
_marker: ::std::marker::PhantomData<(#phantom_generics)>,
|
||||
}
|
||||
#struct_def
|
||||
|
||||
// we cannot disable any lints here because it will be applied to the function body
|
||||
// as well.
|
||||
#(#component_impl_attrs)*
|
||||
impl #impl_generics ::yew::functional::FunctionProvider for #component_name #ty_generics #where_clause {
|
||||
type Properties = #props_type;
|
||||
|
||||
fn run(#ctx_ident: &mut ::yew::functional::HookContext, #component_props: &Self::Properties) -> ::yew::html::HtmlResult {
|
||||
#func
|
||||
|
||||
::yew::html::IntoHtmlResult::into_html_result(#fn_name #fn_generics (#ctx_ident, #component_props))
|
||||
}
|
||||
}
|
||||
|
||||
#into_comp_impl
|
||||
#provider_fn_impl
|
||||
#debug_impl
|
||||
#base_comp_impl
|
||||
};
|
||||
|
||||
Ok(quoted)
|
||||
|
||||
@ -92,7 +92,7 @@ impl ToTokens for HtmlComponent {
|
||||
children,
|
||||
} = self;
|
||||
|
||||
let props_ty = quote_spanned!(ty.span()=> <#ty as ::yew::html::IntoComponent>::Properties);
|
||||
let props_ty = quote_spanned!(ty.span()=> <#ty as ::yew::html::BaseComponent>::Properties);
|
||||
let children_renderer = if children.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
||||
@ -19,26 +19,26 @@ error[E0599]: no method named `build` found for struct `PropsBuilder<PropsBuilde
|
||||
= note: the method was found for
|
||||
- `PropsBuilder<PropsBuilderStepPropsBuilder>`
|
||||
|
||||
error[E0277]: the trait bound `Comp<MissingTypeBounds>: IntoComponent` is not satisfied
|
||||
error[E0277]: the trait bound `Comp<MissingTypeBounds>: yew::BaseComponent` is not satisfied
|
||||
--> tests/function_component_attr/generic-props-fail.rs:27:14
|
||||
|
|
||||
27 | html! { <Comp<MissingTypeBounds> /> };
|
||||
| ^^^^ the trait `IntoComponent` is not implemented for `Comp<MissingTypeBounds>`
|
||||
| ^^^^ the trait `yew::BaseComponent` is not implemented for `Comp<MissingTypeBounds>`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<Comp<P> as IntoComponent>
|
||||
<Comp<P> as yew::BaseComponent>
|
||||
|
||||
error[E0599]: the function or associated item `new` exists for struct `VChild<Comp<MissingTypeBounds>>`, but its trait bounds were not satisfied
|
||||
--> tests/function_component_attr/generic-props-fail.rs:27:14
|
||||
|
|
||||
8 | #[function_component(Comp)]
|
||||
| --------------------------- doesn't satisfy `Comp<MissingTypeBounds>: IntoComponent`
|
||||
| --------------------------- doesn't satisfy `Comp<MissingTypeBounds>: yew::BaseComponent`
|
||||
...
|
||||
27 | html! { <Comp<MissingTypeBounds> /> };
|
||||
| ^^^^ function or associated item cannot be called on `VChild<Comp<MissingTypeBounds>>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Comp<MissingTypeBounds>: IntoComponent`
|
||||
`Comp<MissingTypeBounds>: yew::BaseComponent`
|
||||
|
||||
error[E0277]: the trait bound `MissingTypeBounds: yew::Properties` is not satisfied
|
||||
--> tests/function_component_attr/generic-props-fail.rs:27:14
|
||||
|
||||
@ -10,10 +10,10 @@ error[E0599]: the function or associated item `new` exists for struct `VChild<Un
|
||||
--> tests/html_macro/component-unimplemented-fail.rs:6:14
|
||||
|
|
||||
3 | struct Unimplemented;
|
||||
| --------------------- doesn't satisfy `Unimplemented: IntoComponent`
|
||||
| --------------------- doesn't satisfy `Unimplemented: BaseComponent`
|
||||
...
|
||||
6 | html! { <Unimplemented /> };
|
||||
| ^^^^^^^^^^^^^ function or associated item cannot be called on `VChild<Unimplemented>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Unimplemented: IntoComponent`
|
||||
`Unimplemented: BaseComponent`
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::dom_bundle::BSubtree;
|
||||
use crate::html::Scoped;
|
||||
use crate::html::{IntoComponent, NodeRef, Scope};
|
||||
use crate::html::{BaseComponent, NodeRef, Scope};
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use web_sys::Element;
|
||||
@ -10,20 +10,20 @@ use web_sys::Element;
|
||||
/// An instance of an application.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(documenting, doc(cfg(feature = "csr")))]
|
||||
pub struct AppHandle<ICOMP: IntoComponent> {
|
||||
pub struct AppHandle<COMP: BaseComponent> {
|
||||
/// `Scope` holder
|
||||
pub(crate) scope: Scope<<ICOMP as IntoComponent>::Component>,
|
||||
pub(crate) scope: Scope<COMP>,
|
||||
}
|
||||
|
||||
impl<ICOMP> AppHandle<ICOMP>
|
||||
impl<COMP> AppHandle<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
/// The main entry point of a Yew program which also allows passing properties. It works
|
||||
/// similarly to the `program` function in Elm. You should provide an initial model, `update`
|
||||
/// function which will update the state of the model and a `view` function which
|
||||
/// will render the model to a virtual DOM tree.
|
||||
pub(crate) fn mount_with_props(host: Element, props: Rc<ICOMP::Properties>) -> Self {
|
||||
pub(crate) fn mount_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
|
||||
clear_element(&host);
|
||||
let app = Self {
|
||||
scope: Scope::new(None),
|
||||
@ -46,11 +46,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> Deref for AppHandle<ICOMP>
|
||||
impl<COMP> Deref for AppHandle<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
type Target = Scope<<ICOMP as IntoComponent>::Component>;
|
||||
type Target = Scope<COMP>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.scope
|
||||
@ -71,11 +71,11 @@ mod feat_hydration {
|
||||
|
||||
use crate::dom_bundle::Fragment;
|
||||
|
||||
impl<ICOMP> AppHandle<ICOMP>
|
||||
impl<COMP> AppHandle<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
pub(crate) fn hydrate_with_props(host: Element, props: Rc<ICOMP::Properties>) -> Self {
|
||||
pub(crate) fn hydrate_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
|
||||
let app = Self {
|
||||
scope: Scope::new(None),
|
||||
};
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! More details about function components and Hooks can be found on [Yew Docs](https://yew.rs/docs/next/concepts/function-components/introduction)
|
||||
|
||||
use crate::html::{AnyScope, BaseComponent, HtmlResult};
|
||||
use crate::html::{AnyScope, BaseComponent, Context, HtmlResult};
|
||||
use crate::Properties;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
@ -31,9 +31,6 @@ use wasm_bindgen::prelude::*;
|
||||
mod hooks;
|
||||
pub use hooks::*;
|
||||
|
||||
use crate::html::sealed::SealedBaseComponent;
|
||||
use crate::html::Context;
|
||||
|
||||
/// This attribute creates a function component from a normal Rust function.
|
||||
///
|
||||
/// Functions with this attribute **must** return `Html` and can optionally take an argument for props.
|
||||
@ -206,36 +203,36 @@ pub trait FunctionProvider {
|
||||
fn run(ctx: &mut HookContext, props: &Self::Properties) -> HtmlResult;
|
||||
}
|
||||
|
||||
/// Wrapper that allows a struct implementing [`FunctionProvider`] to be consumed as a component.
|
||||
/// A type that interacts [`FunctionProvider`] to provide lifecycle events to be bridged to
|
||||
/// [`BaseComponent`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Function Components should not be implemented with this type directly.
|
||||
///
|
||||
/// Use the `#[function_component]` macro instead.
|
||||
#[doc(hidden)]
|
||||
pub struct FunctionComponent<T>
|
||||
where
|
||||
T: FunctionProvider + 'static,
|
||||
T: FunctionProvider,
|
||||
{
|
||||
_never: std::marker::PhantomData<T>,
|
||||
hook_ctx: RefCell<HookContext>,
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for FunctionComponent<T>
|
||||
impl<T> FunctionComponent<T>
|
||||
where
|
||||
T: FunctionProvider + 'static,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("FunctionComponent<_>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BaseComponent for FunctionComponent<T>
|
||||
where
|
||||
T: FunctionProvider + 'static,
|
||||
{
|
||||
type Message = ();
|
||||
type Properties = T::Properties;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
/// Creates a new function component.
|
||||
pub fn new(ctx: &Context<T>) -> Self
|
||||
where
|
||||
T: BaseComponent<Message = ()> + FunctionProvider + 'static,
|
||||
{
|
||||
let scope = AnyScope::from(ctx.link().clone());
|
||||
|
||||
let re_render = {
|
||||
let link = ctx.link().clone();
|
||||
|
||||
Rc::new(move || link.send_message(()))
|
||||
};
|
||||
|
||||
@ -245,16 +242,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> HtmlResult {
|
||||
let props = ctx.props();
|
||||
/// Renders a function component.
|
||||
pub fn render(&self, props: &T::Properties) -> HtmlResult {
|
||||
let mut hook_ctx = self.hook_ctx.borrow_mut();
|
||||
|
||||
hook_ctx.prepare_run();
|
||||
@ -268,15 +257,24 @@ where
|
||||
result
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
/// Run Effects of a function component.
|
||||
pub fn rendered(&self) {
|
||||
let hook_ctx = self.hook_ctx.borrow();
|
||||
hook_ctx.run_effects();
|
||||
}
|
||||
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {
|
||||
/// Destroys the function component.
|
||||
pub fn destroy(&self) {
|
||||
let mut hook_ctx = self.hook_ctx.borrow_mut();
|
||||
hook_ctx.drain_states();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SealedBaseComponent for FunctionComponent<T> where T: FunctionProvider + 'static {}
|
||||
impl<T> fmt::Debug for FunctionComponent<T>
|
||||
where
|
||||
T: FunctionProvider + 'static,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("FunctionComponent<_>")
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::function_component;
|
||||
use crate::html;
|
||||
use crate::html::{ChildrenProps, Html, IntoComponent};
|
||||
use crate::html::{BaseComponent, ChildrenProps, Html};
|
||||
|
||||
/// A Component to represent a component that does not exist in current implementation.
|
||||
///
|
||||
@ -141,7 +141,7 @@ use crate::html::{ChildrenProps, Html, IntoComponent};
|
||||
#[function_component]
|
||||
pub fn PhantomComponent<T>(props: &ChildrenProps) -> Html
|
||||
where
|
||||
T: IntoComponent,
|
||||
T: BaseComponent,
|
||||
{
|
||||
html! { <>{props.children.clone()}</> }
|
||||
}
|
||||
|
||||
@ -89,19 +89,13 @@ impl<COMP: BaseComponent> Context<COMP> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
/// A Sealed trait that prevents direct implementation of
|
||||
/// [BaseComponent].
|
||||
pub trait SealedBaseComponent {}
|
||||
}
|
||||
|
||||
/// The common base of both function components and struct components.
|
||||
///
|
||||
/// If you are taken here by doc links, you might be looking for [`Component`] or
|
||||
/// [`#[function_component]`](crate::functional::function_component).
|
||||
///
|
||||
/// We provide a blanket implementation of this trait for every member that implements [`Component`].
|
||||
pub trait BaseComponent: sealed::SealedBaseComponent + Sized + 'static {
|
||||
pub trait BaseComponent: Sized + 'static {
|
||||
/// The Component's Message.
|
||||
type Message: 'static;
|
||||
|
||||
@ -221,25 +215,3 @@ where
|
||||
Component::destroy(self, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> sealed::SealedBaseComponent for T where T: Sized + Component + 'static {}
|
||||
|
||||
/// A trait that indicates a type is able to be converted into a component.
|
||||
///
|
||||
/// You may want to use this trait if you want to accept both function components and struct
|
||||
/// components as a generic parameter.
|
||||
pub trait IntoComponent {
|
||||
/// The Component's Properties.
|
||||
type Properties: Properties;
|
||||
|
||||
/// The Component Type.
|
||||
type Component: BaseComponent<Properties = Self::Properties> + 'static;
|
||||
}
|
||||
|
||||
impl<T> IntoComponent for T
|
||||
where
|
||||
T: BaseComponent + 'static,
|
||||
{
|
||||
type Properties = T::Properties;
|
||||
type Component = T;
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ use super::BaseComponent;
|
||||
|
||||
use crate::callback::Callback;
|
||||
use crate::context::{ContextHandle, ContextProvider};
|
||||
use crate::html::IntoComponent;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
@ -58,25 +57,23 @@ impl AnyScope {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the self value can't be cast into the target type.
|
||||
pub fn downcast<ICOMP: IntoComponent>(&self) -> Scope<ICOMP::Component> {
|
||||
self.try_downcast::<ICOMP>().unwrap()
|
||||
pub fn downcast<COMP: BaseComponent>(&self) -> Scope<COMP> {
|
||||
self.try_downcast::<COMP>().unwrap()
|
||||
}
|
||||
|
||||
/// Attempts to downcast into a typed scope
|
||||
///
|
||||
/// Returns [`None`] if the self value can't be cast into the target type.
|
||||
pub fn try_downcast<ICOMP: IntoComponent>(&self) -> Option<Scope<ICOMP::Component>> {
|
||||
self.typed_scope
|
||||
.downcast_ref::<Scope<ICOMP::Component>>()
|
||||
.cloned()
|
||||
pub fn try_downcast<COMP: BaseComponent>(&self) -> Option<Scope<COMP>> {
|
||||
self.typed_scope.downcast_ref::<Scope<COMP>>().cloned()
|
||||
}
|
||||
|
||||
/// Attempts to find a parent scope of a certain type
|
||||
///
|
||||
/// Returns [`None`] if no parent scope with the specified type was found.
|
||||
pub fn find_parent_scope<ICOMP: IntoComponent>(&self) -> Option<Scope<ICOMP::Component>> {
|
||||
pub fn find_parent_scope<COMP: BaseComponent>(&self) -> Option<Scope<COMP>> {
|
||||
iter::successors(Some(self), |scope| scope.get_parent())
|
||||
.find_map(AnyScope::try_downcast::<ICOMP>)
|
||||
.find_map(AnyScope::try_downcast::<COMP>)
|
||||
}
|
||||
|
||||
/// Accesses a value provided by a parent `ContextProvider` component of the
|
||||
|
||||
@ -7,7 +7,6 @@ mod error;
|
||||
mod listener;
|
||||
|
||||
pub use classes::*;
|
||||
pub(crate) use component::sealed;
|
||||
pub use component::*;
|
||||
pub use conversion::*;
|
||||
pub use error::*;
|
||||
|
||||
@ -329,7 +329,7 @@ pub mod prelude {
|
||||
pub use crate::events::*;
|
||||
pub use crate::html::{
|
||||
create_portal, BaseComponent, Children, ChildrenWithProps, Classes, Component, Context,
|
||||
Html, HtmlResult, IntoComponent, NodeRef, Properties,
|
||||
Html, HtmlResult, NodeRef, Properties,
|
||||
};
|
||||
pub use crate::macros::{classes, html, html_nested};
|
||||
pub use crate::suspense::Suspense;
|
||||
|
||||
@ -5,7 +5,7 @@ use std::rc::Rc;
|
||||
use web_sys::Element;
|
||||
|
||||
use crate::app_handle::AppHandle;
|
||||
use crate::html::IntoComponent;
|
||||
use crate::html::BaseComponent;
|
||||
|
||||
thread_local! {
|
||||
static PANIC_HOOK_IS_SET: Cell<bool> = Cell::new(false);
|
||||
@ -32,28 +32,28 @@ fn set_default_panic_hook() {
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(documenting, doc(cfg(feature = "csr")))]
|
||||
#[must_use = "Renderer does nothing unless render() is called."]
|
||||
pub struct Renderer<ICOMP>
|
||||
pub struct Renderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent + 'static,
|
||||
COMP: BaseComponent + 'static,
|
||||
{
|
||||
root: Element,
|
||||
props: ICOMP::Properties,
|
||||
props: COMP::Properties,
|
||||
}
|
||||
|
||||
impl<ICOMP> Default for Renderer<ICOMP>
|
||||
impl<COMP> Default for Renderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent + 'static,
|
||||
ICOMP::Properties: Default,
|
||||
COMP: BaseComponent + 'static,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::with_props(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> Renderer<ICOMP>
|
||||
impl<COMP> Renderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent + 'static,
|
||||
ICOMP::Properties: Default,
|
||||
COMP: BaseComponent + 'static,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
/// Creates a [Renderer] that renders into the document body with default properties.
|
||||
pub fn new() -> Self {
|
||||
@ -66,12 +66,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> Renderer<ICOMP>
|
||||
impl<COMP> Renderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent + 'static,
|
||||
COMP: BaseComponent + 'static,
|
||||
{
|
||||
/// Creates a [Renderer] that renders into the document body with custom properties.
|
||||
pub fn with_props(props: ICOMP::Properties) -> Self {
|
||||
pub fn with_props(props: COMP::Properties) -> Self {
|
||||
Self::with_root_and_props(
|
||||
gloo_utils::document()
|
||||
.body()
|
||||
@ -82,14 +82,14 @@ where
|
||||
}
|
||||
|
||||
/// Creates a [Renderer] that renders into a custom root with custom properties.
|
||||
pub fn with_root_and_props(root: Element, props: ICOMP::Properties) -> Self {
|
||||
pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
|
||||
Self { root, props }
|
||||
}
|
||||
|
||||
/// Renders the application.
|
||||
pub fn render(self) -> AppHandle<ICOMP> {
|
||||
pub fn render(self) -> AppHandle<COMP> {
|
||||
set_default_panic_hook();
|
||||
AppHandle::<ICOMP>::mount_with_props(self.root, Rc::new(self.props))
|
||||
AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,14 +98,14 @@ where
|
||||
mod feat_hydration {
|
||||
use super::*;
|
||||
|
||||
impl<ICOMP> Renderer<ICOMP>
|
||||
impl<COMP> Renderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent + 'static,
|
||||
COMP: BaseComponent + 'static,
|
||||
{
|
||||
/// Hydrates the application.
|
||||
pub fn hydrate(self) -> AppHandle<ICOMP> {
|
||||
pub fn hydrate(self) -> AppHandle<COMP> {
|
||||
set_default_panic_hook();
|
||||
AppHandle::<ICOMP>::hydrate_with_props(self.root, Rc::new(self.props))
|
||||
AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,30 @@
|
||||
use super::*;
|
||||
|
||||
use crate::html::Scope;
|
||||
use crate::html::{BaseComponent, Scope};
|
||||
|
||||
/// A Yew Server-side Renderer.
|
||||
#[cfg_attr(documenting, doc(cfg(feature = "ssr")))]
|
||||
#[derive(Debug)]
|
||||
pub struct ServerRenderer<ICOMP>
|
||||
pub struct ServerRenderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
props: ICOMP::Properties,
|
||||
props: COMP::Properties,
|
||||
hydratable: bool,
|
||||
}
|
||||
|
||||
impl<ICOMP> Default for ServerRenderer<ICOMP>
|
||||
impl<COMP> Default for ServerRenderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
ICOMP::Properties: Default,
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::with_props(ICOMP::Properties::default())
|
||||
Self::with_props(COMP::Properties::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> ServerRenderer<ICOMP>
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
ICOMP::Properties: Default,
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with default properties.
|
||||
pub fn new() -> Self {
|
||||
@ -34,12 +32,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> ServerRenderer<ICOMP>
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with custom properties.
|
||||
pub fn with_props(props: ICOMP::Properties) -> Self {
|
||||
pub fn with_props(props: COMP::Properties) -> Self {
|
||||
Self {
|
||||
props,
|
||||
hydratable: true,
|
||||
@ -69,7 +67,7 @@ where
|
||||
|
||||
/// Renders Yew Application to a String.
|
||||
pub async fn render_to_string(self, w: &mut String) {
|
||||
let scope = Scope::<<ICOMP as IntoComponent>::Component>::new(None);
|
||||
let scope = Scope::<COMP>::new(None);
|
||||
scope
|
||||
.render_to_string(w, self.props.into(), self.hydratable)
|
||||
.await;
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
use crate::html::{Children, Html, Properties};
|
||||
|
||||
/// Properties for [Suspense].
|
||||
#[derive(Properties, PartialEq, Debug, Clone)]
|
||||
pub struct SuspenseProps {
|
||||
/// The Children of the current Suspense Component.
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
|
||||
/// The Fallback UI of the current Suspense Component.
|
||||
#[prop_or_default]
|
||||
pub fallback: Html,
|
||||
}
|
||||
|
||||
@ -6,6 +6,6 @@ mod suspension;
|
||||
|
||||
#[cfg(any(feature = "csr", feature = "ssr"))]
|
||||
pub(crate) use component::BaseSuspense;
|
||||
pub use component::Suspense;
|
||||
pub use component::{Suspense, SuspenseProps};
|
||||
pub use hooks::*;
|
||||
pub use suspension::{Suspension, SuspensionHandle, SuspensionResult};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! This module contains the implementation of a virtual component (`VComp`).
|
||||
|
||||
use super::Key;
|
||||
use crate::html::{BaseComponent, IntoComponent, NodeRef};
|
||||
use crate::html::{BaseComponent, NodeRef};
|
||||
use std::any::TypeId;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
@ -158,15 +158,15 @@ impl<COMP: BaseComponent> Mountable for PropsWrapper<COMP> {
|
||||
}
|
||||
|
||||
/// A virtual child component.
|
||||
pub struct VChild<ICOMP: IntoComponent> {
|
||||
pub struct VChild<COMP: BaseComponent> {
|
||||
/// The component properties
|
||||
pub props: Rc<ICOMP::Properties>,
|
||||
pub props: Rc<COMP::Properties>,
|
||||
/// Reference to the mounted node
|
||||
node_ref: NodeRef,
|
||||
key: Option<Key>,
|
||||
}
|
||||
|
||||
impl<ICOMP: IntoComponent> Clone for VChild<ICOMP> {
|
||||
impl<COMP: BaseComponent> Clone for VChild<COMP> {
|
||||
fn clone(&self) -> Self {
|
||||
VChild {
|
||||
props: Rc::clone(&self.props),
|
||||
@ -176,21 +176,21 @@ impl<ICOMP: IntoComponent> Clone for VChild<ICOMP> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP: IntoComponent> PartialEq for VChild<ICOMP>
|
||||
impl<COMP: BaseComponent> PartialEq for VChild<COMP>
|
||||
where
|
||||
ICOMP::Properties: PartialEq,
|
||||
COMP::Properties: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &VChild<ICOMP>) -> bool {
|
||||
fn eq(&self, other: &VChild<COMP>) -> bool {
|
||||
self.props == other.props
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> VChild<ICOMP>
|
||||
impl<COMP> VChild<COMP>
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
/// Creates a child component that can be accessed and modified by its parent.
|
||||
pub fn new(props: ICOMP::Properties, node_ref: NodeRef, key: Option<Key>) -> Self {
|
||||
pub fn new(props: COMP::Properties, node_ref: NodeRef, key: Option<Key>) -> Self {
|
||||
Self {
|
||||
props: Rc::new(props),
|
||||
node_ref,
|
||||
@ -199,25 +199,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> From<VChild<ICOMP>> for VComp
|
||||
impl<COMP> From<VChild<COMP>> for VComp
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
fn from(vchild: VChild<ICOMP>) -> Self {
|
||||
VComp::new::<ICOMP>(vchild.props, vchild.node_ref, vchild.key)
|
||||
fn from(vchild: VChild<COMP>) -> Self {
|
||||
VComp::new::<COMP>(vchild.props, vchild.node_ref, vchild.key)
|
||||
}
|
||||
}
|
||||
|
||||
impl VComp {
|
||||
/// Creates a new `VComp` instance.
|
||||
pub fn new<ICOMP>(props: Rc<ICOMP::Properties>, node_ref: NodeRef, key: Option<Key>) -> Self
|
||||
pub fn new<COMP>(props: Rc<COMP::Properties>, node_ref: NodeRef, key: Option<Key>) -> Self
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
VComp {
|
||||
type_id: TypeId::of::<ICOMP::Component>(),
|
||||
type_id: TypeId::of::<COMP>(),
|
||||
node_ref,
|
||||
mountable: Box::new(PropsWrapper::<ICOMP::Component>::new(props)),
|
||||
mountable: Box::new(PropsWrapper::<COMP>::new(props)),
|
||||
key,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! This module contains the implementation of abstract virtual node.
|
||||
|
||||
use super::{Key, VChild, VComp, VList, VPortal, VSuspense, VTag, VText};
|
||||
use crate::html::IntoComponent;
|
||||
use crate::html::BaseComponent;
|
||||
use std::cmp::PartialEq;
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
@ -93,11 +93,11 @@ impl From<VPortal> for VNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ICOMP> From<VChild<ICOMP>> for VNode
|
||||
impl<COMP> From<VChild<COMP>> for VNode
|
||||
where
|
||||
ICOMP: IntoComponent,
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
fn from(vchild: VChild<ICOMP>) -> Self {
|
||||
fn from(vchild: VChild<COMP>) -> Self {
|
||||
VNode::VComp(VComp::from(vchild))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
use yew::html::BaseComponent;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub struct Comp;
|
||||
|
||||
impl BaseComponent for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
false
|
||||
}
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
fn view(&self, _ctx: &Context<Self>) -> HtmlResult {
|
||||
todo!()
|
||||
}
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {}
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@ -1,12 +0,0 @@
|
||||
error[E0277]: the trait bound `Comp: yew::Component` is not satisfied
|
||||
--> tests/failed_tests/base_component_impl-fail.rs:6:6
|
||||
|
|
||||
6 | impl BaseComponent for Comp {
|
||||
| ^^^^^^^^^^^^^ the trait `yew::Component` is not implemented for `Comp`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `html::component::sealed::SealedBaseComponent` for `Comp`
|
||||
note: required by a bound in `BaseComponent`
|
||||
--> src/html/component/mod.rs
|
||||
|
|
||||
| pub trait BaseComponent: sealed::SealedBaseComponent + Sized + 'static {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `BaseComponent`
|
||||
@ -1,28 +0,0 @@
|
||||
use yew::html::BaseComponent;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub struct Comp;
|
||||
|
||||
impl BaseComponent for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
false
|
||||
}
|
||||
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
fn view(&self, _ctx: &Context<Self>) -> HtmlResult {
|
||||
todo!()
|
||||
}
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {}
|
||||
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
||||
}
|
||||
|
||||
impl yew::html::component::sealed::SealedBaseComponent for Comp {}
|
||||
|
||||
fn main() {}
|
||||
@ -1,11 +0,0 @@
|
||||
error[E0603]: module `component` is private
|
||||
--> tests/failed_tests/sealed_base_component_impl-fail.rs:26:17
|
||||
|
|
||||
26 | impl yew::html::component::sealed::SealedBaseComponent for Comp {}
|
||||
| ^^^^^^^^^ private module
|
||||
|
|
||||
note: the module `component` is defined here
|
||||
--> src/html/mod.rs
|
||||
|
|
||||
| mod component;
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -190,7 +190,7 @@ async fn hydration_with_suspense() {
|
||||
// still hydrating, during hydration, the server rendered result is shown.
|
||||
assert_eq!(
|
||||
result.as_str(),
|
||||
r#"<!--<[yew::functional::FunctionComponent<hydration::hydration_with_suspense::{{closure}}::Content>]>--><div class="content-area"><div class="actual-result">0</div><button class="increase">increase</button><div class="action-area"><button class="take-a-break">Take a break!</button></div></div><!--</[yew::functional::FunctionComponent<hydration::hydration_with_suspense::{{closure}}::Content>]>-->"#
|
||||
r#"<!--<[hydration::hydration_with_suspense::{{closure}}::Content]>--><div class="content-area"><div class="actual-result">0</div><button class="increase">increase</button><div class="action-area"><button class="take-a-break">Take a break!</button></div></div><!--</[hydration::hydration_with_suspense::{{closure}}::Content]>-->"#
|
||||
);
|
||||
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
@ -475,7 +475,7 @@ async fn hydration_nested_suspense_works() {
|
||||
let result = obtain_result();
|
||||
assert_eq!(
|
||||
result.as_str(),
|
||||
r#"<!--<[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::Content>]>--><div class="content-area"><div class="action-area"><button class="take-a-break">Take a break!</button></div><!--<[yew::functional::FunctionComponent<yew::suspense::component::feat_csr_ssr::Suspense>]>--><!--<[yew::suspense::component::feat_csr_ssr::BaseSuspense]>--><!--<?>--><!--<[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::InnerContent>]>--><div class="content-area"><div class="action-area"><button class="take-a-break2">Take a break!</button></div></div><!--</[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::InnerContent>]>--><!--</?>--><!--</[yew::suspense::component::feat_csr_ssr::BaseSuspense]>--><!--</[yew::functional::FunctionComponent<yew::suspense::component::feat_csr_ssr::Suspense>]>--></div><!--</[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::Content>]>-->"#
|
||||
r#"<!--<[hydration::hydration_nested_suspense_works::{{closure}}::Content]>--><div class="content-area"><div class="action-area"><button class="take-a-break">Take a break!</button></div><!--<[yew::suspense::component::feat_csr_ssr::Suspense]>--><!--<[yew::suspense::component::feat_csr_ssr::BaseSuspense]>--><!--<?>--><!--<[hydration::hydration_nested_suspense_works::{{closure}}::InnerContent]>--><div class="content-area"><div class="action-area"><button class="take-a-break2">Take a break!</button></div></div><!--</[hydration::hydration_nested_suspense_works::{{closure}}::InnerContent]>--><!--</?>--><!--</[yew::suspense::component::feat_csr_ssr::BaseSuspense]>--><!--</[yew::suspense::component::feat_csr_ssr::Suspense]>--></div><!--</[hydration::hydration_nested_suspense_works::{{closure}}::Content]>-->"#
|
||||
);
|
||||
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
@ -484,7 +484,7 @@ async fn hydration_nested_suspense_works() {
|
||||
let result = obtain_result();
|
||||
assert_eq!(
|
||||
result.as_str(),
|
||||
r#"<div class="content-area"><div class="action-area"><button class="take-a-break">Take a break!</button></div><!--<[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::InnerContent>]>--><div class="content-area"><div class="action-area"><button class="take-a-break2">Take a break!</button></div></div><!--</[yew::functional::FunctionComponent<hydration::hydration_nested_suspense_works::{{closure}}::InnerContent>]>--></div>"#
|
||||
r#"<div class="content-area"><div class="action-area"><button class="take-a-break">Take a break!</button></div><!--<[hydration::hydration_nested_suspense_works::{{closure}}::InnerContent]>--><div class="content-area"><div class="action-area"><button class="take-a-break2">Take a break!</button></div></div><!--</[hydration::hydration_nested_suspense_works::{{closure}}::InnerContent]>--></div>"#
|
||||
);
|
||||
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[rustversion::attr(stable(1.56), test)]
|
||||
fn native_failed_tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/failed_tests/*-fail.rs");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user