S
SolidJS4mo ago
Eike

"Hydrate" custom HTML

I would like to make previously-static HTML interactive. As an example for what I want, suppose the server sends HTML like
<ul class="todo-list">
<li>Learn SolidJS</li>
<li>Create TODO-App</li>
<li>Get rich and famous</li>
</ul>
<ul class="todo-list">
<li>Learn SolidJS</li>
<li>Create TODO-App</li>
<li>Get rich and famous</li>
</ul>
I would like to make this interactive by: - Adding a "Done" button to each list item - Adding a list item with an empty textbox at the end to add additional todos to the list. Ideally, I don’t need to adapt the HTML. (The real case is more complicated, of course, but I’m relatively confident that I could take it from there.) I assume that I need to read in the existing list in some way to fill an internal data structure, then render it again using SolidJS, adding in the buttons and extra list item at the end. So far, I pass in the <ul> element into my component function, parse the list items and then call replaceChildren on the <ul> to get rid of the existing HTML. Then, I can return new <li> elements in a fragment that have the buttons and so on. Like so:
const App: Component<{root: Element}> = ({ root }) => {
let initialTodos: Array<string> = [];
Array.from(root.children).forEach(child => {
initialTodos.push(child.textContent || "")
})
const [todos, setTodos] = createSignal(initialTodos);
root.replaceChildren();
return <>
<For each={todos()}>
{(todo) => <li>{todo} <button>Done!</button></li>}
</For>
<li><input placeholder='Add a todo' /></li>
</>
};
const App: Component<{root: Element}> = ({ root }) => {
let initialTodos: Array<string> = [];
Array.from(root.children).forEach(child => {
initialTodos.push(child.textContent || "")
})
const [todos, setTodos] = createSignal(initialTodos);
root.replaceChildren();
return <>
<For each={todos()}>
{(todo) => <li>{todo} <button>Done!</button></li>}
</For>
<li><input placeholder='Add a todo' /></li>
</>
};
In then render this into the existing <ul class="todo-list">. Ideally this would work with the development server. However, when I change script and it live-reloads, it is easy to confuse Solid or your own code as it now runs on the previous iterations output instead of the list from the HTML. For example, here, every iteration will insert another "Done!" at the end of todo list items. Is there a better way?
3 Replies
Erik Demaine
Erik Demaine4mo ago
This seems like a reasonable way to do it client-side. (In particular, I don't think you can use Solid's hydration here.) I assume you have reasons not to do this augmentation server-side?
Eike
EikeOP4mo ago
I do. (Mostly that I just like to send out "pure" versions for people who don't have JavaScript enabled. Long-term, this is for crosswords and other pencil puzzles where such users can then just print them out and solve on paper. I also don't necessarily want to burden other users of the script with setting up server-side rendering.) I was just wondering whether there was a better way. But if there isn't, and I just have to give up live-reload for the final version, that's fine. (For the largest part of development, I can still use it, just not when doing the "import".) Thanks!
Erik Demaine
Erik Demaine4mo ago
Just because I don't see a better way doesn't mean there isn't one, of course 🙂 By "live reload" I assume you're referring to HMR? I agree you'd probably need to use /* @refresh reload */

Did you find this page helpful?