ux icon indicating copy to clipboard operation
ux copied to clipboard

[LiveComponent] getComponent is undefined if the controller loads very quickly as explained in the docs

Open gremo opened this issue 4 months ago • 6 comments

Based on the docs the getComponent method should be called during initialize. Becase it's a Promise the method initialize should be async.

When the component loads very quickly, this.component will be undefined in connect:

import { Controller } from '@hotwired/stimulus';
import { getComponent } from '@symfony/ux-live-component';

export default class extends Controller {
  async initialize() {
    this.component = await getComponent(this.element);
  }

  connect() {
    console.log(this.component); // undefined
  }
}

Adding async connect won't solve the problem. Adding a small delay will solve the problem. Another way is to call getComponent inside async connect. Either way, the documentation is a bit "obscure" about this and devs without a good JavaScript understanding will have the same issue.

I don't know if this is a bug or the way Stimulus work, but I was expecting that connect will be called after initialize resolve.

gremo avatar Feb 23 '24 16:02 gremo

Any update or workaround? Still in the official documentation...

gremo avatar Mar 16 '24 10:03 gremo

Instead of using connect there, you should rely on the events dispatched by the LiveComponent

smnandre avatar Mar 16 '24 14:03 smnandre

What do you mean @smnandre ? It's the official example in Symfony docs!

gremo avatar Mar 16 '24 14:03 gremo

The documentation is see

// assets/controllers/some-custom-controller.js
// ...
import { getComponent } from '@symfony/ux-live-component';

export default class extends Controller {
    async initialize() {
        this.component = await getComponent(this.element);

        this.component.on('render:finished', (component) => {
            // do something after the component re-renders
        });
    }
}

smnandre avatar Mar 16 '24 14:03 smnandre

And it's a bit... skinny. What if I need component.emit based on some javascript event? Where would you put that logic?

onPaste() {
  this.component.emitSelf("paste", { value });
}

Inside onPaste, happens somtime that this.component is undefined, based on how fast the controller loads.

gremo avatar Mar 16 '24 14:03 gremo

async initialize() {
        this.component = await getComponent(this.element);


        // THERE


        this.component.on('render:finished', (component) => {
            // do something after the component re-renders
        });
    }

smnandre avatar Mar 16 '24 19:03 smnandre