WebReflection / uland

A µhtml take at neverland
ISC License
108 stars 2 forks source link

Callback ref #11

Closed alexey13 closed 3 years ago

alexey13 commented 3 years ago

Good day, Andrea!

I find that it hard to control with useEffect and useRef dom element connection and disconnection (article about it). The React propose to use CallbackRef for such cases. I tried to use useCallback for ref but looks like the logic how ref in uland work is different, because it calls callback on every render (in pen see console). And as I understood right it should call it on node connection only.

https://codepen.io/alexzhav/pen/XWNBYQe

WebReflection commented 3 years ago

Makes sense, and the issue was in uhandlers being too greedy with callbacks invokes ... the linked MR explains it all.

Thank you, it's fixed now, it should work as you expect in 0.9 👋

alexey13 commented 3 years ago

Andrea just to be clear. Now if we use function as a "ref" it will run only on connected, on disconnected the function will not trigger, right?

WebReflection commented 3 years ago

right ... but in uland you rarely have a disconnected case ... a basic example would help answering, but basically that's what it happens now, there's no much logic with the ref, just a way to simulate React but not 100% React behavior.

Although, with uconnect you can setup a disconnect dance for any node you like, and you'd do that within the ref, as it's called once per node.

I hope this helps.

alexey13 commented 3 years ago

It helped a lot, thank you!

WebReflection commented 3 years ago

@alexey13 this made me think that maybe onconnect and ondisconnect events would be welcome in here ... as it is for lighterhtml-plus module ... what do you think? I am not sure it would add too much bloat, but maybe a uland-plus module won't hurt ... or a uhtml-plus, for what it matters, with uland-plus based on that instead 🤔

WebReflection commented 3 years ago

P.S. alternatively ... does React ref=${callback} works like useEffect, so that if the callback returns a callback that one will be used when the element is disconnected? maybe I can try something similar, but uconnect is more robust than that, imho.

alexey13 commented 3 years ago

P.S. alternatively ... does React ref=${callback} works like useEffect, so that if the callback returns a callback that one will be used when the element is disconnected? maybe I can try something similar, but uconnect is more robust than that, imho.

React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.

I think that because uland work a little different we should not use callbacks in useEffect (in React they used to run ondisconnected, like you said), right?

But i quite good to use uconnect in such cases.

WebReflection commented 3 years ago

I need to think about this ... useEffect now runs per render, but that’s a uhooks-dom design that indeed makes little sense in the DOM context, compared to generic uhooks that doesn’t know a thing about DOM ... interesting

jaschaio commented 3 years ago

@alexey13 this made me think that maybe onconnect and ondisconnect events would be welcome in here ... as it is for lighterhtml-plus module ... what do you think? I am not sure it would add too much bloat, but maybe a uland-plus module won't hurt ... or a uhtml-plus, for what it matters, with uland-plus based on that instead 🤔

+1 on that idea. Just not sure if as a -plus version as I feel that people get discouraged by not understanding the differences between all the libraries and different versions

WebReflection commented 3 years ago

more a hack than a proper solution .... but would this work?

https://codepen.io/WebReflection/pen/yLgVBpL?editors=0011

when there is an effect you can listen to disconnect event on a node.

alexey13 commented 3 years ago

Andrea, can you describe what this line do? useEffect(Boolean, []);

WebReflection commented 3 years ago

It’s a hack to enable listeners on the returned node ... because useEffect needs to know when an element is removed from the DOM, and enforce teardown fx

alexey13 commented 3 years ago

Oh, i get it. Checked source code and understood the logic. You add disconnected event when useeffect in the component. Cool.

WebReflection commented 3 years ago

A proper/better approach would boil down to this:

const no = () => {}, op = [], once = {once: true};
const useMount = ({connected, disconnected}) => {
  useEffect(no);
  return useCallback(node => {
    if (connected)
      connected(node);
    if (disconnected)
      node.addEventListener(
        'disconnected', () => { disconnected(node); }, once
      );
  }, op);
};
alexey13 commented 3 years ago

Good evening! This one in my use case show the best results. It use uconnect.

import { observer } from './utils.js';

export const useMount = ({connected, disconnected}) => {
  const ref = useRef(null);

  const setRef = useCallback(node => {
    observer.connect(node, {
      connected(event) {
        if(connected) {
          if(ref.current) {
            disconnected(ref.current);
          }
          connected(node);
        }
        ref.current = node;
      },
      disconnected(event) {
        if(disconnected) {
          disconnected(node);
        }
        ref.current = null;
      }
    });
  }, []);

  return [setRef, ref];
};