WebReflection / augmentor

Extensible, general purpose, React like hooks for the masses.
https://medium.com/@WebReflection/demystifying-hooks-f55ad885609f
ISC License
134 stars 7 forks source link

useState/render issue with arrays. #17

Closed ripter closed 4 years ago

ripter commented 5 years ago

Example: https://codepen.io/ripter/pen/LKpbgd?editors=0011

const [history, setHistory] = useState([0]); with setHistory(history.concat([i])); triggers the re-render as expected. The console.logs show the data has been updated; but the html is rendered as if the data were empty.

Click the button in the example to see the list disappear instead of rendering the new item. And the button does not update to show the new value of i.

WebReflection commented 5 years ago

please check https://github.com/WebReflection/augmentor#augmentor as currently I have zero time to fix anything in here. apologies for any inconvenience

WebReflection commented 4 years ago

I've finally found the culprit of this issue, after a whole refactoring and redesign of augmentor.

This page shows a working example https://github.com/WebReflection/lighterhtml/blob/master/test/aug.html

The reason your example doesn't work, is that you cannot actually have more than a single html usage per each component, otherwise references go upside down (inside loop first, container after) and everything breaks.

This behavior is well explained in this article too: https://inventingwithmonster.io/20190207-break-the-rules-of-react-hooks/#running-hooks-within-a-loop

I am going to close this, as there's literally nothing to do for me here, just eventually provide better examples of how to use augmentor with lighterhtml.

ripter commented 4 years ago

So are you saying the problem is because there is the nested html in there?

return html`<ol id="controls">
    ${ingredients.map((name) => html`<li>
      <h1>${Title(name)}</h1>
      <h2>Count:
        ${ingredients.reduce((a, i) => a + (i === name ? 1 : 0), 0)}
      </h2>
      <div style="display: none;">
        <button onclick=${remove(name)}>Remove</button>
      </div>
    </li>`)}
  </ol>`;

If I understand that correctly, each time I want to use html I have to make it it's own component?

WebReflection commented 4 years ago

If I understand that correctly, each time I want to use html I have to make it it's own component?

Yes, it's a limitation of the useRef logic. I might find a better work-around in the future, but right now you need to write your example like this one: https://codepen.io/WebReflection/pen/KKKoOKO?editors=0011

WebReflection commented 4 years ago

P.S. the easiest way to create and use components and hooks is neverland, which uses dom-augmentor so that useEffect works as expected out of the box.

WebReflection commented 4 years ago

P.S.2 another approach is to use some extra feature of lighterhtml as I've done in here https://codepen.io/WebReflection/pen/wvvmVgZ?editors=0011

The key is in the difference between inner and outer html definition.

const {render, hook, Hole} = lighterhtml;
const {html, svg} = hook(useRef);
const inner = (...args) => new Hole('html', args);

In this way you don't need to use extra components, but you do need to remember to use inner within the render:

  return html`<div>
  <button onclick=${() => {
    setHistory(history.concat([i]));
  }}>${text} ${i}</button></div>
  <ul>${history.map(num => inner`<li>${num}</li>`)}</ul>`;

Having one component looks somehow cleaner, but I think I might use this example to improve neverland too.

WebReflection commented 4 years ago

If interested, the next release should have:

const {render, hook} = lighterhtml;
const {html, svg, inner} = hook(useRef);

and the ability to write the following:

  return html`<div>
  <button onclick=${() => {
    setHistory(history.concat([i]));
  }}>${text} ${i}</button></div>
  <ul>${history.map(num => inner.html`<li>${num}</li>`)}</ul>`;
WebReflection commented 4 years ago

So ... inner exported now, you can see it live here https://codepen.io/WebReflection/pen/wvvmVgZ?editors=0011