Search code examples
reactjspreact

Procedurally generate html elements with Preact and HTM


I'm using Preact with HTM (no compiler required) and am having trouble looping through an object and creating a DOM element for each item.

What is the correct way to procedurally generate DOM elements with Preact + HTM?

import { h, Component, render } from 'https://unpkg.com/preact?module';
import htm from 'https://unpkg.com/htm?module'; 
const html = htm.bind(h);

function componentValues() {
  var elements = {e1:10, e2:20}; 
  var objEditor = '<div class="row">';
  for (const key in elements) {
    objEditor += '<div class="col">'+key+'</div>';
  }
  objEditor += '</div>';
  return objEditor;
}

function renderPage() {
  render(html`
      <div class="container-xl">
        <p>Hello World</p>
        <${componentValues} />
      </div>`, document.getElementById("app"));
}

renderPage();

My result is this

Hello World

<div class="row"><div class="col">e1</div><div class="col">e2</div></div>

https://codepen.io/28raining/pen/WNyaJrL


Solution

  • Are you looking to use HTML strings in JSX or simply find the most idiomatic way to go about generating new entries?

    If this is the latter (as your description seems to indicate), you definitely want to avoid using strings. You have JSX at your disposal; use it to template out your content rather than string concatenation.

    Try this:

    function componentValues() {
      var elements = {e1:10, e2:20};
      return html`
        <div class="row">
          ${Object.entries(elements).map(([key, value]) =>
            html`<div class="col">${key}</div>`
          )}
        </div>
      `;
    }
    

    If you did need to use HTML strings (it happens, though should be avoided if at all possible), that string isn't a JSX element/component, so you can't use it as one. Instead, you need to wrap it with html:

    function renderPage() {
      render(html`
        <div class="container-xl">
          <p>Hello World</p>
          ${html([componentValues()])}
        </div>`,
        document.getElementById("app")
      );
    }
    

    Tagged template functions (html in this case) get arguments as arrays, hence why we create a new array and add the result of componentValues() to it.