max-mapper / max-mapper/yo-yo

querySelector/ID component design

Open
#15 15 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
1.3k
Forks
46
PR merge metrics
No merged PRs in 30d

Description

So I've been experimenting a lot with life cycles and yoyo components. So far the most reliable component design I've found is to use randomly generated or pre-assigned ID's, querySelection and the MutationObserver.

Here is an example component:

const Component = function (_yield) {
    var id = "id" + parseInt(Math.random()*1000000)
    var open = true

    function onload () {
      console.log(id + "loaded!")
    }

    function onupdate () {
      console.log(id + "mutated")
    }

    function onunload () {
      console.log(id + "unloaded!")
    }

    function toggle () {
      open = !open;
      yo.update(document.querySelector("#"+id), render(_yield))
    }

    function render (_yield) {
      return yo`
        <div id=${id}>
          ${id}
          <button onclick=${toggle}>Toggle</button>
          <span> ${open && yo`<div> It's Open </div>` || yo`<div> It's Closed </div>`}</span>
          <hr />
          ${_yield}
        </div>
        `
    }

    onmutation(id, onload, onunload, onupdate)
    return render(_yield)
}

My reusable MutationObserver which tracks when the component loads, unloads and updates:

var watch = []
if (window && window.MutationObserver) {
  var observer = new MutationObserver(function (mutations) {
    for(var i = 0; i < watch.length; i++) {
      var selector = document.querySelector(watch[i][0]);
      if(selector && !watch[i][4]) {
        watch[i][4] = 1
        watch[i][1](watch[i])
      }else if(!selector && watch[i][4]) {
        watch[i][2](watch[i])
        watch.splice(i, 1)
      }
    }
    for (var i = 0; i < mutations.length; i++) {
      var mutation = mutations[i]
      for(var i = 0; i < watch.length; i++)
        if(watch[i][0].substr(1) == mutation.target.id) watch[i][3](watch[i])
    }
  })
  observer.observe(document.body, {childList: true, subtree: true})
}
function onmutation (el, l, u, m) {
  l = l || function () {}
  u = u || function () {}
  m = m || function () {}
  watch.push([(el[0] != "#" && "#" + el || el), l, u, m, 0])
}

-- 335 bytes min+gz

I believe this to be the most reliable way to make stand alone components that update themselves.

Central Reason: if your component internally selects a node and not an ID, the dom can be morphed from above, altering the selected node, without changing the original node target in the component. The component then becomes useless, as its event methods are targeting a dom node that no longer exists. I ran into this problem constantly when I was using yo.update on higher level components, which didn't update the node targets of the morphed lower level components.

Using ID's and querySelecting seems to be the most reliable, light-weight and simple solution to this problem. This MutationObserver pattern is similar to @shama's on-load, but it uses querySelecting and not an actual dom node.

I didn't find the morphdom-hooks to be reliable either, as they would look for a dom node target that would sometimes not exist. This is because higher level hooks dont walk the dom lower and update component targets below. Diablo is a system that does this. However, it follows the react pattern fairly heavily, and I couldn't get it running without issues. I'm not huge on a react knock off either for my components.

Would love to get thoughts on this design and a name for my querySelector mutationobserver =D?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the component example and the MutationObserver implementation in the issue body. The issue asks for feedback and a name for the design, but it names no repository file, test, entry point, or concrete acceptance criteria, so a definition of done is not established.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.