Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat(esl-utils): getEventListeners() method for SynteticEventTarget #1548

Merged
merged 9 commits into from
Mar 31, 2023

Conversation

fshovchko
Copy link
Contributor

@fshovchko fshovchko commented Mar 14, 2023

Protected method to get event listeners for SynteticEventTarget
Closes: #1540

@fshovchko fshovchko requested review from a team, ala-n, julia-murashko and yadamskaya and removed request for a team March 14, 2023 11:13
@fshovchko fshovchko self-assigned this Mar 14, 2023
@fshovchko fshovchko added the enhancement Improvements and additions to code label Mar 14, 2023
@fshovchko fshovchko requested a review from ala-n March 14, 2023 17:59
@fshovchko fshovchko requested review from dshovchko and NastaLeo March 14, 2023 20:18
@ala-n ala-n added the needs review The PR is waiting for review label Mar 15, 2023
@ala-n
Copy link
Collaborator

ala-n commented Mar 17, 2023

Hey, @exadel-inc/esl-all-contributors team the is a small stylistic question appears in this PR, so we need your feedback to decide the final way for our update.
The question relates to the uniqueness property in the getEventListener tech implementation.
We need it from the API point of view independently of passing the actual event or getting all listeners.
But technically, we have some details.

Option 1 (👍🏻)
Currently in PR
For consistency, we have used the uniq for parametrized and general flow.

protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] {
    return uniq((typeof type !== 'string') ? flat(Object.values(this._listeners)) : (this._listeners[type] || []));
}

At the same time, we do not use getEventListeners(type) inside the dispatchEvent as it is a significant performance loss
dispatchEvent can be called frequently, so creating a Set under the hood is not a good idea

// dispatchEvent part
   this._listeners[e.type]?.forEach((listener) => {
     if (typeof listener === 'function') listener.call(this, e);
     else listener.handleEvent.call(listener, e);
   });

But potentially, we share overoptimized getEventListeners with the user.
(But just to be clear, this is an edge case that user access listeners and do it so frequently)

Option 2 (🎉)
We do not make extra operations inside the getEventListeners

protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] {
   if (typeof type !== 'string') return uniq(flat(Object.values(this._listeners)));
   return this._listeners[type] || [];
}

The collections (arrays) in the private store this._listeners are unique separately as they are modified by addEventListener / removeEventListener accordingly (saving uniq state)

Then we can safely use it in dispatchEvent

// dispatchEvent part
   this.getEventListeners(e.type).forEach((listener) => {
     if (typeof listener === 'function') listener.call(this, e);
     else listener.handleEvent.call(listener, e);
   });

But again, potentially, the key this._listeners are still accessible and can contain invalid data (edge case too)

As additional options, we can also consider
Option 3 (👀) = Option 2 + store listeners under the true private symbol (the lightweight enough change, but do we need it)

Option 4 (🚀) use a Set to store listeners (but according to the specific of the event target, the only frequent operation is getting listeners for a defined event type, so there is may no need to use mo strict collection)

Please share your opinion with marked emoji or in the tread (here or Slack)

@ala-n
Copy link
Collaborator

ala-n commented Mar 29, 2023

According to the votes, let's go with the following update for the getEventListener tech implementation

  1. No extra processing on the internal collection
protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] {
   if (typeof type !== 'string') return uniq(flat(Object.values(this._listeners)));
   return this._listeners[type] || [];
}
  1. Simplify dispatch
// dispatchEvent part
   this.getEventListeners(e.type).forEach((listener) => {
     if (typeof listener === 'function') listener.call(this, e);
     else listener.handleEvent.call(listener, e);
   });
  1. Make this._listeners stored under the hidden Symbol key instead (true private implementation)

@ala-n ala-n requested review from dshovchko and yadamskaya March 29, 2023 22:35
@ala-n ala-n added the require squash No action from PR author required. Marks that current PR will be merged with squash label Mar 29, 2023
@fshovchko fshovchko requested a review from ala-n March 30, 2023 14:21
@codeclimate
Copy link

codeclimate bot commented Mar 31, 2023

Code Climate has analyzed commit b2b061d and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 100.0% (50% is the threshold).

This pull request will bring the total coverage in the repository to 48.4% (0.0% change).

View more on Code Climate.

@ala-n ala-n merged commit c293b61 into main Mar 31, 2023
@ala-n ala-n deleted the feat/synthetic-get-listeners branch March 31, 2023 00:37
@github-actions github-actions bot locked and limited conversation to collaborators Mar 31, 2023
@ala-n
Copy link
Collaborator

ala-n commented Apr 12, 2023

🎉 This PR is included in version 4.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@ala-n ala-n added the released label Apr 12, 2023
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
enhancement Improvements and additions to code needs review The PR is waiting for review released require squash No action from PR author required. Marks that current PR will be merged with squash
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[🚀esl-utils]: Proposal: getEventListeners() method for SynteticEventTarget
6 participants