-
This is just a question, as there's probably a solution to this, but I'm not sure what's really going on here I've got this React code: // test.tsx
"use client";
import { useEffect, useState } from "react";
export function Foo() {
const [foo, setFoo] = useState<HTMLDivElement | null>(null);
const isFormControl = foo ? Boolean(foo.closest("form")) : true;
console.log(isFormControl);
useEffect(() => {
console.log(isFormControl);
}, [isFormControl]);
return (
<>
<div ref={(node) => setFoo(node)}>hello world</div>
{isFormControl && <div>hello world 2</div>}
</>
);
}
// HomePage.tsx
import { Foo } from "./test";
export default function HomePage() {
return (
<>
{/* <form> */}
<Foo />
{/* </form> */}
{/* <form> */}
<Foo />
{/* </form> */}
<>
);
} Which I've ported like so: #[component]
fn Foo() -> impl IntoView {
let foo_ref = NodeRef::<html::Div>::new();
let is_form_control = move || {
if let Some(foo) = foo_ref.get() {
foo.closest("form").ok().flatten().is_some()
} else {
true
}
};
logging::log!("{}", is_form_control());
Effect::new(move |_| {
logging::log!("{}", is_form_control());
});
view! {
<div node_ref=foo_ref>"hello world"</div>
<Show when=is_form_control>
<div>"hello world 2"</div>
</Show>
}
}
#[component]
fn HomePage() -> impl IntoView {
view! {
// <form>
<Foo />
// </form>
// <form>
<Foo />
// </form>
}
} The React code (using Next.js) works fine, but it doesn't seem like the case with the Leptos port because there are hydration issues 🤔 I've also tested this with Solid.js (with SolidStart) just to see if I'm thinking of the reactive system in the wrong way, but Solid also doesn't have any hydration issues // test.tsx
import { Show, createEffect, createSignal } from "solid-js";
export default function Foo() {
const [foo, setFoo] = createSignal<HTMLDivElement | null>(null);
const isFormControl = () => (foo() ? Boolean(foo()!.closest("form")) : true);
console.log(isFormControl());
createEffect(() => {
console.log(isFormControl());
});
return (
<>
<div ref={node => setFoo(node)}>hello world</div>
<Show when={isFormControl()}>
<div class="test">hello world 2?</div>
</Show>
</>
);
}
// HomePage.tsx
import { Foo } from "./test";
export default function HomePage() {
return (
<>
{/* <form> */}
<Foo />
{/* </form> */}
{/* <form> */}
<Foo />
{/* </form> */}
</>
);
} (albeit my Solid.js code doesn't seem to work properly when a form is detected, isFormControl always evaluates to false; at least there aren't any hydration issues 🤣) So I'm wondering if I'm actually doing anything wrong here or if something more is going on? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
As you can see from the logs when you run the Leptos version, you are rendering different things on the server and the client -- Your React code, on the other hand, renders once and then immediately renders a second time (with the Here's a Leptos version that seems to work fine and does the same thing as the React one: rendering immediately with #[component]
fn Foo() -> impl IntoView {
let foo_ref = NodeRef::<html::Div>::new();
let (is_form_control, set_is_form_control) = create_signal(true);
Effect::new(move |_| {
logging::log!("{}", is_form_control());
});
Effect::new(move |_| {
if let Some(foo) = foo_ref.get() {
foo.closest("form").ok().flatten().is_some()
} else {
true
}
});
view! {
<div node_ref=foo_ref>"hello world"</div>
<Show when=is_form_control>
<div>"hello world 2"</div>
</Show>
}
} |
Beta Was this translation helpful? Give feedback.
As you can see from the logs when you run the Leptos version, you are rendering different things on the server and the client --
is_form_control
is alwaystrue
on the server (because there is no DOM node to stick in the node ref), but by the time you have reached theShow
on the client you have already filled theNodeRef
sois_form_control
isfalse
unless it's in a<form>
.Your React code, on the other hand, renders once and then immediately renders a second time (with the
setState
). I am not a React user but this is what I understand is happening in your component.Here's a Leptos version that seems to work fine and does the same thing as the React one: rendering immediately with
is_form…