OnMount fired twice for the same component

Hey hey, how it is going? I have this weird situation where I'm seeing a component being mounted twice. I was even able to see 2 DOM nodes mounted and I'd like to collect feedback if this has ever been a thing of it's just a me issue, doing non standard stuff. Code will follow:
No description
21 Replies
Chris, the 0.5x engineer
type FeedbackFormState = {
rate: number;
comment: string;
};

export interface FeedbackModalProps {
open: boolean;
onSubmit: (data: FeedbackFormState) => void;
onDismiss: () => void;
isSubmitting: boolean;
isSuccess: boolean;
sessionId: string;
}

export function FeedbackModal(props: FeedbackModalProps) {
const [dismissed, setIsDismissed] = createSignal(false);
const [formState, setFormState] = createStore<FeedbackFormState>({
rate: 0,
comment: "",
});

function onDismiss() {
// props.onDismiss();
setIsDismissed(true);
console.log("{DEBUG}", "Dismissed")
}

function onSubmit() {
props.onSubmit(formState);
}

onMount(() => {
console.log("{DEBUG}", "Mouting feedback component", props.sessionId);
});

onCleanup(() => {
console.log("{DEBUG}", "Cleanup feedback");
});

return (
<Modal.Root
open={props.open && !dismissed()}
</Modal.Root>
);
}
type FeedbackFormState = {
rate: number;
comment: string;
};

export interface FeedbackModalProps {
open: boolean;
onSubmit: (data: FeedbackFormState) => void;
onDismiss: () => void;
isSubmitting: boolean;
isSuccess: boolean;
sessionId: string;
}

export function FeedbackModal(props: FeedbackModalProps) {
const [dismissed, setIsDismissed] = createSignal(false);
const [formState, setFormState] = createStore<FeedbackFormState>({
rate: 0,
comment: "",
});

function onDismiss() {
// props.onDismiss();
setIsDismissed(true);
console.log("{DEBUG}", "Dismissed")
}

function onSubmit() {
props.onSubmit(formState);
}

onMount(() => {
console.log("{DEBUG}", "Mouting feedback component", props.sessionId);
});

onCleanup(() => {
console.log("{DEBUG}", "Cleanup feedback");
});

return (
<Modal.Root
open={props.open && !dismissed()}
</Modal.Root>
);
}
This is the component being mounted twice Modal root is a component that renders a <dialog>
jer3m01
jer3m01•2mo ago
Can you show where you're using the component?
peerreynders
peerreynders•2mo ago
I'm seeing a component being mounted twice. I was even able to see 2 DOM nodes mounted
This suggests to me that there are two instances of the component rather than onMount running twice.
Chris, the 0.5x engineer
Sure @jer3m01
export default function Session() {
const params = useParams<{ id: string }>();
const queryClient = useQueryClient();
const sessionId = () => params.id;

const { session, nextQuestion, clearQuestion, endSession, rateSession } =
useSession(sessionId);

onMount(() => {
console.log("{DEBUG}", "Mount Session page");
console.log("{DEBUG}", "Initial data value", session.data);
});

createEffect(() => {
console.log("{DEBUG}", "UPDATED SESSION DATA", session.data);
});

return (
<AppPageContainer>
<Show when={session.data}>
{(sessionData) => {
console.log(" ======== Executing show callback with ", sessionData());
return (
<>
<FeedbackModal
open={
Boolean(sessionData().doneAt) &&
!sessionData().ratingSubmitted
}
sessionId={sessionData().id}
// onDismiss={() => rateSession.mutate(null)}
// onSubmit={(data) => rateSession.mutateAsync(data)}
// isSubmitting={rateSession.isPending}
// isSuccess={rateSession.isSuccess}
/>
</>
);
}}
</Show>
</AppPageContainer>
);
}
export default function Session() {
const params = useParams<{ id: string }>();
const queryClient = useQueryClient();
const sessionId = () => params.id;

const { session, nextQuestion, clearQuestion, endSession, rateSession } =
useSession(sessionId);

onMount(() => {
console.log("{DEBUG}", "Mount Session page");
console.log("{DEBUG}", "Initial data value", session.data);
});

createEffect(() => {
console.log("{DEBUG}", "UPDATED SESSION DATA", session.data);
});

return (
<AppPageContainer>
<Show when={session.data}>
{(sessionData) => {
console.log(" ======== Executing show callback with ", sessionData());
return (
<>
<FeedbackModal
open={
Boolean(sessionData().doneAt) &&
!sessionData().ratingSubmitted
}
sessionId={sessionData().id}
// onDismiss={() => rateSession.mutate(null)}
// onSubmit={(data) => rateSession.mutateAsync(data)}
// isSubmitting={rateSession.isPending}
// isSuccess={rateSession.isSuccess}
/>
</>
);
}}
</Show>
</AppPageContainer>
);
}
@peerreynders I'm thinking the same thing! I was able to identify where it does come from, the <Show> callback is runnig TWICE! Oh yeah using a ternary condition to render when session.data is defined fixes my issue
bigmistqke 🌈
bigmistqke 🌈•2mo ago
can you wrap console.log(" ======== Executing show callback with ", sessionData()); in an onMount?
Chris, the 0.5x engineer
Yessir, give me one sec I'll be sharing my console output for all the scenarios
Chris, the 0.5x engineer
On page load, when I navigate on my problematic page, this is what I have, the show callback is fired twice and it does generate 2 instances of the feedback modal
No description
Chris, the 0.5x engineer
exact same thing, with a ternary fixes the issue
No description
Chris, the 0.5x engineer
Now there's only one feedback modal My session.data coming from Tanstack-Query, is a proxy object
bigmistqke 🌈
bigmistqke 🌈•2mo ago
🤔
Chris, the 0.5x engineer
I KNOW RIGHT
bigmistqke 🌈
bigmistqke 🌈•2mo ago
can u try to make a minimal reproduction in the solid playground?
Chris, the 0.5x engineer
Yep I'm working on it, tbh no success atm
bigmistqke 🌈
bigmistqke 🌈•2mo ago
something fishy is going on 🙂
peerreynders
peerreynders•2mo ago
Could a reactive cycle cause this? The modal seems to always exists once the session has been acquired. The difference is that modal isn't open once it has been dismissed.
Chris, the 0.5x engineer
Ok gentlemen I found the culprit The reconcile mechanism of tanstack query for Solid
Chris, the 0.5x engineer
GitHub
[solid-query] Using data in a show causes double-rendering after a ...
Describe the bug This might be a bug in Solid itself, but when passing a query's data to a <Show> with a function child, the child gets ran multiple times without being cleaned up properl...
Chris, the 0.5x engineer
GitHub
feat(solid-query): Rework internals of createBaseQuery (#7272) · Ta...
* feat(solid-query): Rework internals of createBaseQuery This change aims to simplify and enhance the internals of createBaseQuery. This change is a precursor to fix a couple of pressing issu...
Chris, the 0.5x engineer
Thanks y'all! Chatting with y'all helped a lot lol. I just had to be vocal about it Glad I can ship my feedback modal today 🔥
The modal seems to always exists once the session has been acquired.
True and this was a side effect of my debugging journey. I'll be restoring the expected behaviour which is, if the modal is closed, it's not being rendered AT ALL
bigmistqke 🌈
bigmistqke 🌈•2mo ago
congrats on squashing that bug!
Chris, the 0.5x engineer
Thanks!