link or navigation in Toasts or modals Error : router primitives can be only used inside a Route
I create a modal system. it was so OK! until i use <A/> tag
18 Replies
and Error : router primitives can be only used inside a Route
i also check solid-toast to see how it handle it and boom even links in solid-toast has same problem
toast.success(<A href="">link</A>);
import { useModal } from "~/hooks/useModal";
import { ButtonRound } from "../components/button/ButtonRound";
import { IconLogo } from "../components/icons/IconLogo";
import ModalAccount from "../components/modal/modal.account";
// import { useModal } from "../context/modal.context";
import { useAccount } from "../hooks/useAccount";
import toast from "solid-toast";
import { A } from "@solidjs/router";
export function Account() {
const { data } = useAccount();
const { onModal, onClear, modal } = useModal();
function onModalAccount() {
onModal(<ModalAccount onCloseModal={onClear} />);
toast.success(<A href="">link</A>);
}
return (
<button class="flex items-center justify-center gap-4" onClick={onModalAccount}>
<ButtonRound>
<IconLogo />
</ButtonRound>
{data() && <p class="text-xs">{data()?.data?.name}</p>}
{/* {modal()} */}
</button>
);
}
import { useLocation } from "@solidjs/router";
import { createContext, useContext, JSXElement, ParentComponent, createSignal, createEffect, Show } from "solid-js";
import { Portal } from "solid-js/web";
type ModalContextType = {
onModal: (modal: JSXElement) => void;
onClear: () => void;
rawModal: () => JSXElement | undefined;
};
const ModalContext = createContext<ModalContextType>();
export const ModalProvider: ParentComponent = (props) => {
const [rawModal, setRawModal] = createSignal<JSXElement | undefined>();
const location = useLocation();
function onModal(modal: JSXElement) {
setRawModal(modal);
}
function onClear() {
setRawModal(undefined);
}
createEffect(() => {
location.pathname;
onClear();
});
const value: ModalContextType = {
onModal,
onClear,
rawModal,
};
return <ModalContext.Provider value={value}>{props.children}</ModalContext.Provider>;
};
export function useModal() {
const context = useContext(ModalContext);
if (!context) {
throw new Error("useModal must be used within a ModalProvider");
}
const { onClear, onModal } = context;
return { onClear, onModal };
}
export function ModalInit() {
const context = useContext(ModalContext);
if (!context) {
throw new Error("useModal must be used within a ModalProvider");
}
return (
<Portal>
<Show when={!!context.rawModal()}>{context.rawModal()}</Show>
</Portal>
);
}
import { ButtonRound } from "./ButtonRound";
import { IconChat } from "../icons/IconChat";
import { useModal } from "~/hooks/useModal";
import { A } from "@solidjs/router";
export default function ButtonChat(props: { route?: string }) {
const { onModal, onClear } = useModal();
function onModalChat() {
onModal(
<div class="flex flex-col gap-4 p-4 py-8 w-full h-full overflow-y-auto bg-white" onClick={(e) => e.stopPropagation()}>
<A href="/reserved">my reserved</A>
</div>
);
}
return (
<ButtonRound onClick={onModalChat}>
<IconChat />
</ButtonRound>
);
}1. Did you place the ModalProvider inside the router?
2. maybe ModalInit should make use of the children helper to not call the
context.rawModal() twice.yes, i do
export default function App() {
return (
<Router
root={(props) => (
<ModalProvider>
<Suspense>
<AuthChecker>{props.children}</AuthChecker>
{/* <ModalProvider /> */}
<Toaster position="top-center" />
</Suspense>
<ModalInit />
</ModalProvider>
)}
>
<FileRoutes />
</Router>
);
}
and my modal work correctly until use <A/> or Navigator inside it!
can describe more about it please!
"maybe ModalInit should make use of the children helper to not call the context.rawModal() twice."
I can reproduce the error using Solid-UI's toast which uses Kobalte's toast. The children thing does not matter.
What I found is if you pass the toast content as a function ts will complain but it'll work with the
A from the router and the useprimitives like useLocation
just try this
import toast from "solid-toast";
import { A } from "@solidjs/router";
export function Account() {
function onModalAccount() {
toast.success(<A href="">link</A>);
}
return (
<button class="flex items-center justify-center gap-4" onClick={onModalAccount}>
make toast
</button>
);
}
it use "solid-toast" and when toast <A/> tag reproduce my modal error!??I can reproduce it.
The solution is to pass the message as function instead of the JSX Element
it worked many tanks
i simplified my modal like react-toast
but Error still exist
<A> and 'use' router primitives can be only used inside a Route.
can help me fix it
createRoot causes this. The new root breaks the router context.in the first, I try without createRoot.
but it dos not work, then look at solid-toast and they use createRoot so I try this
I found some tricks against use <A/> or navigation to make my app works, but is not a real solution for this . I am react experienced but new to solid. please help me solve and more important understand it π
Whatβs the relation between Modal and Solid-Toast?
I am afk. But I suppose it could be the initialisation of createStore outside a component that could cause errors.
Try moving it inside a component and share the state with a Context .
This will also prevent memory leaks.
It you need a reference for a Modal component see
https://kobalte.dev/docs/core/components/dialog
or GitHub
https://github.com/kobaltedev/kobalte/tree/main/packages/core/src/dialog
GitHub
kobalte/packages/core/src/dialog at main Β· kobaltedev/kobalte
A UI toolkit for building accessible web apps and design systems with SolidJS. - kobaltedev/kobalte
The problem is you're still creating the JSX element outside of
ModalRoot, try making the store hold a () => JSX.Elementπ
I tried context first. And use toast just for refrence as part of app behive like modals.
Thanks
i will read kobalte
OK! And thanks π
Can give an example please?
I don't have much xp in solidπ
the
() => modal is necessary to stop setStore from executing the modal function itselfThanks π
I will try this way
the key thing is that the
<A> (which does useContext internally) needs to be evaluated underneath the Router.
with all your other setups the JSX was being evaluated in the event handler