Skip to content

Instantly share code, notes, and snippets.

@jpwilliams
Last active September 12, 2017 11:06
Show Gist options
  • Save jpwilliams/d08e285c93b16e9a31a37001fa124b89 to your computer and use it in GitHub Desktop.
Save jpwilliams/d08e285c93b16e9a31a37001fa124b89 to your computer and use it in GitHub Desktop.
EventEmitter with a cache

StoreEmitter

A super-basic EventEmitter that helps track state across components by letting you follow events.

See the code below.

Usage

  • emit now caches values after each emission,
  • get retrieves the last value emitted for the given eventName,
  • on now runs the given callback and returns a value instantly if a value is cached for the passed eventName.

This means that in React, for example, when a new component is created, it's easy to get it up to date with the latest state of the app whilst simultaneously registering a listener for future updates.

import { Component } from 'react'
import StoreEmitter from './StoreEmitter'

class Foo extends Component {
  constructor () {
    super()
    this.state = {
      activeUser: '...'
    }
  }

  componentDidMount () {
    // If activeUser is emitted from anywhere else in the app, run
    // this.changeActiveUser with the new value.
    // If activeUser has been emitted before, this.changeActiveUser
    // will be run immediately with the cached value.
    StoreEmitter.on('activeUser', this.changeActiveUser)
  }

  componentWillUnmount () {
    // Clean up hanging listeners if this component is removed.
    StoreEmitter.removeListener('activeUser', this.changeActiveUser)
  }

  changeActiveUser (user) {
    this.setState({
      activeUser: user
    })
  }

  render () {
    return (
      <div>Active user is {this.state.activeUser}</div>
    )
  }
}

export default Foo

Notes

  • Any breaking changes? Unlike the default EventEmitter, on here always tries to return a cached value and therefore does not return a reference to the EventEmitter itself (this), meaning calls cannot be chained. You can easily change that if you want, though.

  • What's eventemitter3? This example uses eventemitter3 (an open-source rewrite of EventEmitter with higher performance), but you can just as easily use a native EventEmitter too.

  • Why do you create an instance at the end of the file? It may seem odd that the code creates and exports an instance of StoreEmitter, but that's so that everything that requires it receives the same emitter. Otherwise we'd be creating separate emitters for every component! D'oh! 😬

import EventEmitter from 'eventemitter3'
class StoreEmitter extends EventEmitter {
constructor () {
super()
this.cache = {}
}
emit (eventName, ...args) {
this.cache[eventName] = args
return super.emit(eventName, ...args)
}
get (eventName) {
return this.cache[eventName]
}
on (eventName, callback) {
super.on(eventName, callback)
if (this.cache.hasOwnProperty(eventName)) callback(this.cache[eventName])
return this.cache[eventName]
}
}
const storeEmitter = new StoreEmitter()
export default storeEmitter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment