developit / mitt Goto Github PK
View Code? Open in Web Editor NEW🥊 Tiny 200 byte functional event emitter / pubsub.
Home Page: https://npm.im/mitt
License: MIT License
🥊 Tiny 200 byte functional event emitter / pubsub.
Home Page: https://npm.im/mitt
License: MIT License
Hello,
I recently see a change at gatsby codebase where they modified the original mitt source code to use a slightly similar implementation but 25% faster:
https://github.com/gatsbyjs/gatsby/pull/23223/files#diff-57dc49b17585e7e09b4cabd11499e032R29
It could be interesting to implement these changes back to here!
Do would you accept a PR in that direction?
Type definition of WildcardHandler
must be
type WildcardHandler = (type: string, event?: any) => void;
How about emitter.off()
then remove all handlers , and emitter.off('noLonger')
then remove all handlers belong to it?
See tunnckoCoreLabs/dush#5. Because it will trigger listeners twice. See this reddit thread comment.
The simplest fix is to do something like that
emit (name, a, b, c) {
if (name !== '*') {
(all[name] || []).map((handler) => { handler(a, b, c) });
(all['*'] || []).map((handler) => { handler(name, a, b, c) })
}
}
the test case that i made for dush
test('should not allow emitting the wildcard (issue#5)', function (done) {
var emitter = dush()
emitter.on('*', function (name, a, b, c) {
test.strictEqual(name, 'foo')
test.strictEqual(a, 1)
test.strictEqual(b, 2)
test.strictEqual(c, 3)
})
emitter.on('foo', function (a, b, c) {
test.strictEqual(a, 1)
test.strictEqual(b, 2)
test.strictEqual(c, 3)
})
emitter.emit('*', 4, 5, 6)
emitter.emit('foo', 1, 2, 3)
emitter.emit('*', 555)
done()
})
I use a lot of code that you develop and open-source.
I never run into issues that cant be solved by re-reading the docs (and/or in rare cases the code) so I never open issues in here and am using this as a chance to.
I suspect there are many people like me :)
Thanks to @developit ( and all contributors ! ) for the great work !
Cheers 😄
A very interesting library, Small and exquisite, your work is very interesting. Great
i use mitt in typescript, and get an error tip
import mitt from 'mitt'
const emitter: mitt.Emitter = mitt()
then i get
Value of type 'MittStatic' is not callable. Did you mean to include 'new'?ts(2348)
how to fix it, just add new ?
thank you
I would like to be able to use Mitt from the browser like:
<script type=module>
import mitt from 'https://unpkg.com/mitt/es.js';
let emitter = mitt();
...
</script>
Would you be open to a build that that includes the export
. Since this is using rollup that should be pretty easy I think.
would u consider adding this or is it out of scope? Ive tried implementing it myself so I could send a PR, but my minifications skill are not that great ;)
best result i got was 214B
which is exceeding ur goal
Hi!
Im trying to use mitt in our react project using typescript.
But I get this error in runtime:
Uncaught TypeError: mitt is not a constructor
at eval (api.ts:11)
at Object../packages/foo-api/api.ts (bundle.js:10804)
at __webpack_require__ (bundle.js:672)
at fn (foo.js:98)
at eval (index.ts:5)
at Object../packages/foo-api/index.ts (bundle.js:10817)
at __webpack_require__ (bundle.js:672)
at fn (bundle.js:98)
at eval (Foo.view.tsx:15)
at Object../packages/foo-ui/Foo.view.tsx (bundle.js:11779)
I'm using ut just like in your example
import * as mitt from 'mitt'
const emitter: mitt.Emitter = new mitt()
When debugging in Dev tools and check mitt it's an object, not a contructor.
Object {__esModule: true, default: function}
default: function mitt(all )
__esModule: true
__proto__: Object
Am I doing something wrong?
The description for emit method states the following:
If present, "*" handlers are invoked prior to type-matched handlers.
But according to the implementation the order is opposite.
Hey,
I noticed that after when I try to use extends in my TypeScript project, the all class stuff are just removed.
Also I need to use ugly hack to execute the constructor of mitt beacuse TypeError: mitt is not a constructor
.
const mittConstructor = (mitt as any).default.prototype.constructor;
type ExampleClassEmitter = StrictEventEmitter<mitt.Emitter, IExampleEvents>;
class ExampleClass extends (mittConstructor as { new(): ExampleClassEmitter }) {}
So I need to do like this to get inheritance:
const mittConstuctor = (mitt as any).default.prototype.constructor;
class MittFix {
constructor() {
Object.entries(mittConstuctor()).forEach(([key, value]) => {
this[key] = value;
});
}
}
class ExampleClass extends (MittFix as { new(): ExampleClassEmitter }) {}
That's because Mitt constructor returns an object { ... }, and 'this' is not used, but should be.
Regards,
TheAifam5
One of the things I find useful about event emitters/signals is the ability to emit an event/signal only once.
I understand this library aims to have a super small footprint, but I do believe adding such feature would not add significant size to the final bundle.
With that being said, do you have interest in adding this feature? If so, I can attempt a PR.
Thank you.
Just like .emit('foo', 1, 2, 3)
. Initially thought for rest + spread but it adds around 50 bytes.
So I think it may be enough to have explicitly defined 3-4 arguments
That adds 4 bytes.
emit(type, a, b, c) {
list('*').concat(list(type)).forEach( f => { f(a, b, c); });
return ret;
}
Some faster implementations than node core's uses this tactic, but up to 6-8 args
Just looking at the source package of 1.1.3 and the release notes mention that the wildcard typing was added.
Unfortunately, it looks like 1.1.3 still has the old typing definition. Any chance you can trigger a release of 1.1.4 with the updated wildcard typing?
Hello,
I'm here just to ask what is the main usage of the bit operators here? in the off
function you're using >>>
at line 40
all[type].splice(all[type].indexOf(handler) >>> 0, 1);
And in the old source code you used ~
also inside the off
function at line 34
I just want to know why because I can't see a reason to use them and thank you in advance
In the initial release of mitt a caller can register one handler multiple times for an event. For some use cases, though, duplicate registrations can be problematic. At the moment, mitt does not provide any APIs to avoid duplicate registration.
I see two straight forward ways of allowing consumers to avoid duplicate registration:
on()
to take a dedupe parameter. When truthy, mitt would not register the handler if it's already present on all
.has()
method that returns a Boolean indicating if a given handler has been registered for an event. Callers concerned about duplicate handlers can manually check before calling on()
.Between the two I feel like 1 provide a more ergonomic API and would likely help keep byte count down.
Prior art
Hey guys, I opened an issue with React Boilerplate, but thought I'd open one here as well to see if you've ever come across problems uglifying this module installed via npm. Maybe there's a problem with the Webpack config and I just need to add something to make it play nice? Anyway, the boilerplate is one of the most popular boilerplates available for React and it yields a build error when Mitt is either a direct dependency, or any dependent packages are using Mitt.
Here are the steps to reproduce:
$ git clone --depth=1 https://github.com/react-boilerplate/react-boilerplate.git
$ cd react-boilerplate
$ npm run setup
$ npm run clean
$ yarn add mitt
Open react-boilerplate/app/containers/HomePage/index.js and add the following import:
import mitt from 'mitt';
Then,
$ npm run build
The given will be something like this:
ERROR in 1.673e1d9e06dc9452619e.chunk.js from UglifyJs
SyntaxError: Unexpected token punc «(», expected punc «:» [1.673e1d9e06dc9452619e.chunk.js:105,4]
I noticed that #47 fixes some Flow types issues, but that the package.json
is older than that PR. It's causing type errors for us via our svg-baker-runtime
package which uses Mitt as a dependency. We're ignoring Mitt in our .flowconfig
temporarily, but you might want to make sure it's been deployed.
Why to choose Mitt over native EventEmitter?
Just wondering
I really like mitt, and the rewrite in TypeScript is a great step forward, but I wonder if we could go a bit further and allow strong typing of both the event names and their associated values.
Ideally, this would be an opt-in behaviour, meaning that by default mitt uses a string | symbol
type for event names, and any
for values. But passing an interface as a generic type to the constructor would tell mitt what events to expect and what values are associated, along with providing auto-completion in IDEs.
Hypothetical API:
interface MittEvents
{
foo: number
bar: string
egg: {
a: string[]
b?: number
}
noarg: null
}
const emitter = mitt<MittEvents>()
emitter.on('foo', value => {
// value has type `number`
})
emitter.on('bar', value => {
// value has type `string`
})
emitter.on('egg', ({ a, b = 0 }) => {
// a has type `string[]`
// b has type `number` with a default value
})
emitter.emit('foo', 42)
emitter.emit('noarg', null)
// Check value type
emitter.emit('foo', 'not a number') // <- TS error
// Check allowed event names
emitter.emit('another', 42) // <- TS error
Such a typing system can be seen in action in vegemite
, a state management library with a similar EventEmitter interface.
I currently have an implementation using events
and util.inherit
(in the browser, so the npm versions), and would like to use mitt instead, however, I can't seem to figure out what I should use instead of removeAllListeners
.
I guess I could keep a map of listeners myself, but that seems like duplicating what mitt does in the first place.
So far I have:
function DerivedHelper(mainHelper, fn) {
this._emitter = mitt();
}
/**
* Invoke all handlers for the given type.
* If present, `"*"` handlers are invoked after type-matched handlers.
*
* @param {String} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
DerivedHelper.prototype.emit = function(type, data) {
this._emitter.emit(type, data);
};
/**
* Register an event handler for the given type.
*
* @param {String} type Type of event to listen for, or `"*"` for all events
* @param {Function} handler Function to call in response to given event
*/
DerivedHelper.prototype.on = function(type, cb) {
this._emitter.on(type, cb);
};
/**
* Remove an event handler for the given type.
*
* @param {String} type Type of event to unregister `handler` from, or `"*"`
* @param {Function} handler Handler function to remove
*/
DerivedHelper.prototype.off = function(type, cb) {
this._emitter.off(type, cb);
};
/**
* Detach this helper from the main helper
* @return {undefined}
* @throws Error if the derived helper is already detached
*/
DerivedHelper.prototype.detach = function() {
// TODO: find a replacement for this
this.removeAllListeners();
};
Thanks!
shouldnt it remove all listeners instead of only listeners attached with on('*', fn)
? alternatively it would be good to have some destroy
method to remove all listeners, whatsoever, that could be even achieved with emitter.off()
(when called without arguments)
Now that changes are coming it's gonna be crucial that we don't break past implementation without warning users. I propose using something like this: https://github.com/semantic-release/semantic-release
I'm open to help with the integration.
emit(type, ...a) {
list('*').concat(list(type)).forEach( (f) => { f.apply(null, a); });
}
this seems it would be backwards compatible and support multiple arguments
import mitt from 'mitt'
let emitter = mitt()
emitter.on('foo', e => console.log('1') )
emitter.on('foo', e => {throw new Error('oops')})
emitter.on('foo', e => console.log('2') )
emitter.emit('foo', { a: 'b' });
// log: 1
// Error!
https://runkit.com/587db91c9fe155001473453c/587db91c9fe155001473453d
If you want to only target "*"
listeners and do emitter.emit("*", "stuff here")
, those listeners will actually fire twice. Checking for that specific value before firing the "*"
listeners would fix that.
It would be cool and is already some habit for some devs.
const mitt = require('mitt')
const emitter = mitt()
emitter.on('foo', console.log).on('bar', console.log).emit('foo', 123)
Check out this example https://jsfiddle.net/t2knuou6/
When i try to unbind multiple handlers basically immediately it won't remove all of them
It seems currently the dist files aren't in the repo nor in the release zip.
Can you include them in the next release?
(all['*'] || []).slice().map((handler) => { handler(type, evt); });
Why use slice?
This would be useful when you want to run some cleanup function or 'catch all' after all of your matching event handlers have been called.
This code will produce an exception:
const mitt = require("mitt")
const emitter = mitt()
emitter.on('constructor', e => console.log(1))
emitter.emit('constructor', {})
You may want to filter all those native methods out, or use a different approach of collecting the events.
Preact + Mitt Codepen Demo 这个运行不起来
Hello,
should there be a method to remove all listeners of the emitter
, to save performance?
I have an example case when we initialize a component, and the component needs to use the event emitter, and pass it to other function to listen to the component event:
// component.js
componentInit() {
method.init(emitter);
}
// method.js
init (emitter) {
emitter.on('something', doSomething);
}
Then, when the component is about to be destroyed, should the emitter be "cleaned up" so the method won't have to preserve the event listener anymore? For ex:
// component.js
componentDestroy() {
emitter.off('*');
}
Line 57 in bcbe163
It appears that there are some valuable changes in master. Are there bugs or issues preventing a release? I would love to see them published as a new version.
Thanks!
Love seeing a new entry in the space for pubsub/events! Definitely something I keep thinking about.
Have you considered adding support for wildcards to match segments within an event as opposed to being used as a catch-all?
I believe postaljs does this with the #
and *
characters. Probably only need *
to keep things minimalistic, unless there's a reason to differentiate matching one vs. many segments.
Isn't just enough to not use babel and etc? Why we need this and ES6 syntax and imports at all. It is just overhead. With that we can just remove babel from deps and they will look more clean.
/cc @developit
Hi
Extremely cool lib!
It would be great if you write an interactive blog post about mitt. Showing it in action in the browser.
It is super simple to do it with the klipse plugin.
Take a look for instance at my posts about es2017 or setTimeout.
I think...
off(type)
should remove all handlers for the given type
off()
without arguments should just clear the all
object completely
you can just expose all
! it'll only be an addition of 4 chars, and I won't bother you anymore (or at least that's my story) ;P
Hey @developit,
this is a really neat library. I was wondering if it is possible to have a listener fire immediatly if it is attached to an event that already happened? It did not work for me, so is it no supported, or did I do it wrong?
For example I would like to fire an event once the animation library is loaded, so that I can start all animations afterwards, no matter where they are defined.
Thanks for your help.
I believe some babel presets within mitt's package.json are causing following error when used in react-native
NOTE: adding those preset into project itself fixes the issue, but I'm not entirely sure if this it is a good solution to force presets? And it would be good to find out where and how mitt needs these to weight everything.
Loving the project so far <3 I converted my personal project to use mitt alongside slightly altered version of smitty (https://github.com/tkh44/smitty) and it seems much cleaner / easier compared to redux so far, great job 👍
typings.json
during build via package-to-typingsmitt.d.ts
into src/
(it's still included in the npm package).babelrc
into tests/
since that's the only place it's usedyarn.lock
example.js
(started in #37 - should be in the readme instead)/cc @tunnckoCore
Why methods don't rely on this
is a benefit?
Nice little lib. You make extra effort to document the parameters types.
How about typescript ?
// impl
function mitt() {
all = [];
return {
on: (name, handler) => all.push(n => ~['*', n].indexOf(name) ? handler : _ => _),
off: (name, handler) => all = all.filter(h => h(name) !== handler),
emit: (name, args) => all.map(h => h(name)(name, args))
};
}
//tests
const t = mitt();
const handler1 = (name, args) => console.log(name, args);
const handler2 = (name, args) => console.log(name, args);
t.on('*', handler1);
t.on('hello', handler2);
t.emit('hello', {
a: 1
});
t.emit('test', {
a: 2
});
t.off('*', handler1);
t.off('hello', handler2);
t.emit('hello', {
a: 3
});
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.