reactive-singleton

A fancy way to use Singleton pattern within the React environment.

Usage no npm install needed!

<script type="module">
  import reactiveSingleton from 'https://cdn.skypack.dev/reactive-singleton';
</script>

README

Rehoc Logo

Reactive Singleton

Tests status npm version downloads Minified size ES6 Javascript support Typescript support

This is a tool to help you use the Singleton pattern within the React environment. Singleton classes can be used but will not be reactive when any instance attribute is changed. Reactive Singleton comes to solve this problem providing support to Typescript and Javascript projects.

Table of contents

:green_book: Installation

To install the stable version:

Npm:

npm i reactive-singleton

Yarn:

yarn add reactive-singleton

:gear: Main Resources

API

createSingleton

const { watch, setClass } = createSingleton()

class MySingleton {
  private static instance: MySingleton
  public static getInstance(): MySingleton {
    if (!MySingleton.instance) {
      MySingleton.instance = new MySingleton()
    }
    return MySingleton.instance
  }
  public name = 'old name'
  public updateData() {
    /* when data needs to be changed */
    watch((done) => {
      this.name = 'new name'
      done() // Notifies the Singleton Provider
    })
  }
}
/* some singleton class */
setClass(MySingleton)

Used to make the Singleton Pattern be listened to by React. You can implement the Singleton class as usual. After implementing the class, you have to use the first important API resource, it is createSingleton. After calling the createSingleton() method, it'll return the necessary methods to make your service (singleton) works within React:

  • watch: Must be used when data needs to be updated inside the singleton class and watched by React;
  • setClass: Receives the singleton class as parameter. E.g: setClass(MySingletonClass).

Hooks

useSingletonStatus

const status = useSingletonStatus(MySingleton) // "ready" or "in_progress"

This hook will let the app know the current status of a specific singleton's process. Returns "in_progress" when singleton's values are being changed and "ready" when it has been updated.

E.g:

const MyApp = () => {
  // Will return "ready" before useEffect block
  // Will return "in_progress" after useEffect block
  // Will return "ready" after the data be updated inside the singleton instance
  const status = useSingletonStatus(MySingleton)

  const singletonData = MySingleton.getInstance()
  useEffect(() => {
    singletonData.updateData()
  }, [])

  return (/* component elements */)
}

useWasDataUpdated

const wasDataUpdated = useWasDataUpdated(MySingleton) // true or false

// You can optionally define the verification interval (debounce). Default is 500ms
const wasDataUpdated = useWasDataUpdated(MySingleton, 1000) // true or false

Lets the app know if the singleton data was updated. Returns true or false (boolean).

E.g:

const MyApp = () => {
  // Returns 'false' before useEffect block
  // Returns 'true' after useEffect block
  const wasDataUpdated = useWasDataUpdated(MySingleton)

  const singletonData = MySingleton.getInstance()
  useEffect(() => {
    singletonData.updateData()
  }, [])

  return (/* component elements */)
}

useReRenderOnUpdate

useReRenderOnUpdate(MySingleton) // Makes the component re-render every time the Singleton props are updated

The Singleton props can be modified in any level of the app and this hook makes the component re-render every time the props of the Singleton Class are updated. Useful for cases where a component is only reading the Singleton props. The props read by the component will always have the most updated data provided by the Singleton.

E.g:

const MyApp = () => {
  useReRenderOnUpdate(MySingleton)
  const { a, b } = MySingleton.getInstance()

  console.log(a, b) // Will always have the most updated data

  return (/* component elements */)
}

Note: You don't need to use this hook if you're using one of the following resources: withSingleton or useSingletonStatus.

HOC

withSingleton

The withSingleton HOC works the same way as useReRenderOnUpdate Hook. Every time that the Singleton props are changed, the component will re-render using the most updated data provided by the Singleton.

E.g:

const MyComponent = () => {
  const { a, b } = MySingleton.getInstance()
  console.log(a, b) // Will always have the most updated data

  return (/* component elements */)
}

export default withSingleton(MyComponent, MySingleton)

Note: You don't need to use this HOC if you're using one of the following resources: useReRenderOnUpdate or useSingletonStatus.

That's basically this. Feel free to check out the live demos for a better understanding.

:eyes: Live Demos

:stop_sign: Limitations

The only limitation here is, SINGLETON`S METHODS CAN NOT BE DESTRUCTURED. So, use them like:

  const singletonData = MySingleton.getInstance()
  // (!) Methods can't be destructured
  singletonData.updateData()
}

:notebook: Changelogs

v2.0.2

  • Updated important dependencies modules to improve security.

v2.0.0

  • SingletonProvider removed. This is no longer necessary due to changes in performance.

v2.0.0-rc2

  • Fixed the error message that was using an old resource reference

v2.0.0-rc1

  • Fixed the issue that was causing this error: React Hook "useWatcher" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
  • The main API resources provided by createSingleton() was using a resouce called useWatcher to be used inside the Singleton Classes, however this went against React Hooks rules. useWatcher was renamed to watch and the setValue was renamed to setClass. So that, the usage of createSingleton() should be used like:
const { watch, setClass } = createSingleton()
  • Fixed the issue that caused components to re-render more than necessary when using hooks
  • Added new hook: useReRenderOnUpdate
  • Added new HOC: withSingleton
  • New tests

v1.1.7

  • Fixed issue caused by multiple react-dom for ES6

v1.1.6

  • Peer dependencies changed
  • Debounce added to useWasDataUpdated hook

v1.0.5

  • Tests
  • CI
  • Readme doc improved

v1.0.4

  • Order of rendering fixed

v1.0.3

  • Final bundle size was reduced
  • Added JSDoc annotations
  • Minified bundle
  • Merged declarations

v1.0.0

  • First release;

:scroll: License

MIT