S
SolidJS6mo ago
Liquido

How to store JSX element in a signal and render it from other elements

I have created a dialog system using context where the dialog that I want to render is stored via a function provided by context and the provider is the one that renders the dialog:
import { render, Portal } from "solid-js/web";
import { createContext, useContext, createSignal, type JSX, type Accessor } from "solid-js";

export type DialogContextValue = {
openDialog(element: JSX.Element): void;

closeDialog(): void;
};

export const DialogContext = createContext<DialogContextValue>({
openDialog() {},
closeDialog() {},
});

type DialogProviderProps = {
children: JSX.Element
}

export function DialogProvider(props: DialogProviderProps) {
const [activeDialog, setActiveDialog] = createSignal<JSX.Element | undefined>(undefined)

const openDialog = (element: JSX.Element) => {
setActiveDialog(element)
}

const closeDialog = () => {
setActiveDialog(undefined);
}

return (
<DialogContext.Provider value={{ openDialog, closeDialog }}>
{props.children}
{activeDialog && <Portal>{activeDialog()}</Portal>}
</DialogContext.Provider>
);
}

function useDialog() { return useContext(DialogContext); }

function App() {
const dialog = useDialog();
return <button onClick={() => {
dialog.openDialog(<button onClick={dialog.closeDialog}>Dialog open. Click to close</button>);
}}>Open dialog</button>
}
import { render, Portal } from "solid-js/web";
import { createContext, useContext, createSignal, type JSX, type Accessor } from "solid-js";

export type DialogContextValue = {
openDialog(element: JSX.Element): void;

closeDialog(): void;
};

export const DialogContext = createContext<DialogContextValue>({
openDialog() {},
closeDialog() {},
});

type DialogProviderProps = {
children: JSX.Element
}

export function DialogProvider(props: DialogProviderProps) {
const [activeDialog, setActiveDialog] = createSignal<JSX.Element | undefined>(undefined)

const openDialog = (element: JSX.Element) => {
setActiveDialog(element)
}

const closeDialog = () => {
setActiveDialog(undefined);
}

return (
<DialogContext.Provider value={{ openDialog, closeDialog }}>
{props.children}
{activeDialog && <Portal>{activeDialog()}</Portal>}
</DialogContext.Provider>
);
}

function useDialog() { return useContext(DialogContext); }

function App() {
const dialog = useDialog();
return <button onClick={() => {
dialog.openDialog(<button onClick={dialog.closeDialog}>Dialog open. Click to close</button>);
}}>Open dialog</button>
}
Playground Link: https://playground.solidjs.com/anonymous/5d842f3f-3a3c-49b8-8287-9d46e0d29ca1 This works well but I have two problems. Firstly, I get a warning in the console when I trigger the modal:
dev.js:836 computations created outside a createRoot or render will never be disposed
And secondly, some components are not rendered when I open the modal. For example, I installed solid-icons package to render an icon. The icon is not rendered on first open but when I modify the code to trigger hot reload, the icon component is rendered inside it. The icons are rendered fine everywhere else.
4 Replies
Madaxen86
Madaxen866mo ago
Have a look at kobalte.dev It has a unstyled dialog component. You can learn a lot how to design such components and it also has key bindings according the w3c standards e.g. close when pressing ESC,… 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
bigmistqke
bigmistqke6mo ago
Code looks good to me. I can not seem to reproduce the computations-error.
Liquido
LiquidoOP6mo ago
@bigmistqke 🌈 I also tested this code in a sandbox to test it out and it looks like the error is not about the Solid but about Solid Start and SSR. I also checked the code of solid-icons library and it looks like there is an issue with the way the SVGs are rendered in that library with SSR. I will need to investigate that. @Madaxen86 This library is promising for the UI aspect of it but I still want to achieve this centralized dialog system (I can use Kobalte for UI aspect of it for example).
mdynnl
mdynnl6mo ago
instead of directly passing dom nodes, you can pass a component then call it from other side.
Want results from more Discord servers?
Add your server