bazar

0.5.2 • Public • Published

bazar
travis npm MIT PR's welcome gzip size

Bazar

One-to-One and One-to-Many communications between Components.

Bazar is a 500b (framework agnostic) container of connections between js Components. It is designed with React in mind, so are the examples. However you can use Bazar with/without any JS framework of choice. It has been tested with success in connecting React and Vue components inside the same app.

Table of Contents

Installation

With NPM:

  npm install bazar

Without:

  <script src="https://unpkg.com/bazar@0.4.0/dist/bazar.js"></script> 

Demo

TL;DR

One-to-Many:

  1. Some components (a), (b) express an interest in a registered component (c).
  2. (c) updates its state and issue an edict.
  3. (a) and (b) immediately receive the updated (c)'s state.

One-to-One:

  1. A component (a) stores some info useful for the unreachable component (b).
  2. (a) can poke (b) passing the useful info.

Usage

Bazar provides One-to-One and One-to-Many communications between Components through a global store which contains all the required connections to do so. In order to accomplish that you have to:

  1. Register some components.
  2. Dispatch events (either via edict or poke).

Register a component

The id prop is the only strictly requirement to register a new component. However a component registered like that is useless as it cannot edict neither being poked.

import React, { Component } from 'react';
import { register } from 'bazar';
 
class C1 extends Component {
  constructor() {
    super();
    this.state = {};
 
    register({
      id: 'C1' // unique id
    });
  }
 
  render = () => <p>C1</p>
} 

Register a component: can edict

A component with the ability to issue an edict must have a method to sync its state. You can choose which part of the state you prefer to keep in sync.

import React, { Component } from 'react';
import { register, edict } from 'bazar';
 
class C1 extends Component {
  constructor() {
    super();
    this.state = {
      updated: false,
    };
 
    register({
      id: 'C1', // unique id
      sync: () => this.state, // function
    });
  }
 
  // Calling `edict` in `setState` callback will invoke `onEdict` of interested
  // components with the updated state of C1.
  onUpdate = () => this.setState({
    update: true,
  }, () => edict('C1'));
 
  render = () => <button onClick={this.onUpdate}>update</button>
} 

Register a component: can poke

A component with the ability to poke could also be stateless. As the poke function is anonymously invoked.

import React, { Component } from 'react';
import { poke } from 'bazar';
 
const C1 = () => (
  <button 
    onClick={() => poke('registeredComponent', { passed: 'argument' })}>
    update
  </button>
);

Register a component: can getState

A component with the ability to provide its current state must have a valid sync method.

import React, { Component } from 'react';
import { register } from 'bazar';
 
class C1 extends Component {
  constructor() {
    super();
    this.state = { state: 'to keep in sync' }
 
    register({
      id: 'C1',
      sync: () => this.state,
    });
  }
  render = () => <div>C1</div>
};

Register a component: willRerender

To make the presence of multiple components with the same id obvious, Bazar will throw an error when will find some duplicates. When a component rerender will automatically re-register itself in the global store. Pass willRerender: true when registering to notify Bazar that the current component will re-register itself.

import React, { Component } from 'react';
import { register } from 'bazar';
 
class C1 extends Component {
  constructor() {
    super();
    register({
      id: 'C1',
      willRerender: true,
    });
  }
  render = () => <div>C1</div>
};

One-to-Many communication

In the following example:

  1. Registration of 3 components: (a), (b), (c).
  2. (a), (b) express an interest in a registered component (c).
  3. (c) updates its state and issue an edict.
  4. (a) and (b) immediately receive the updated (c)'s state, via onEdict.

note: Usually this kind of communication is useful between STATEFUL components. As the onEdict function comes handy for updating a component's state.

import React, { Component } from 'react';
import { register, edict } from 'bazar';
 
class A extends Component {
  constructor() {
    super();
    register({
      id: 'A',
      interests: ['C'],
      // When C issues an `edict`, this `onEdict` function is invoked.
      // You can update A's state accordingly.
      onEdict: (id, state) => this.setState({ received: true }),
    });
  }
  render = () => <div>A</div>
};
 
class B extends Component {
  constructor() {
    super();
    this.state = { received: false };
    register({
      id: 'B',
      interests: ['C'],
      onEdict: (id, state) => console.log(`I just received a ${state} update from ${id}`),
    });
  }
  render = () => <div>B</div>
};
 
 
class C extends Component {
  constructor() {
    super();
    this.state = { interesting: false };
    register({
      id: 'C',
      sync: () => this.state,
    });
  }
 
  // Issuing an `edict` on setState callBack => so we have access to the just updated state.
  clicked = () => this.setState({ interesting: true }, edict('C'));
 
  render = () => <div onClick={this.clicked}>C</div>
};

One-to-One communication

In the following example:

  1. Registration of 2 (unrelated) components: (a), (b).
  2. A component (a) stores some info useful for the unreachable component (b).
  3. (a) can poke (b) passing the useful info.

note: This kind of communication is useful between a STATELESS component and a STATEFUL one. The poke function is completely anonimous, so it can be invoked from an unregistered component.

import React, { Component } from 'react';
import { register, poke } from 'bazar';
 
const A = (props) => <button onCLick={() => poke('B', props)}>A</button>
 
class B extends Component {
  constructor() {
    super();
    this.state = { empty: {} };
    register({
      id: 'B',
      onPoke: (arg) => this.setState({ empty: arg }),
    });
  }
  render = () => <div>B</div>
};

Get (registered) component's state

Invoking getState you can get the current state of the component belonging to the provided id. If the component is not registered undefined will be returned.

import React, { Component } from 'react';
import { register, getState } from 'bazar';
 
const A = (props) => <button onCLick={() => getState('B')}>A</button>
 
class B extends Component {
  constructor() {
    super();
    this.state = { empty: {} };
    register({
      id: 'B',
      sync: () => this.state,
    });
  }
  render = () => <div>B</div>
};

Clearing store

You can clear bazar store at any time. Useful if connecting many components that will be repeated many times.

e.g. You are connecting widgets of a dashboard. Every widget has an id relative to the dashboard. You render one dashboard at a time but using the same widgets. Clearing the store between dashboard renders prevents ID clashes and let's you reuse the same IDs within the dashboard.

import React, { Component } from 'react';
import { clearStore } from 'bazar';
 
// Call it without arguments.
clearStore();
 

API

register

Most important part of Bazar as it stores all the required informations to connect all the registered components.

param type required default explaination
id string yes unique identifier for the registered Component
sync function no undefined sync state on request
interests array no [] express interests on Components
onEdict function no undefined invoked when a Component in interests issue an edict
onPoke function no undefined invoked when poke is called providing id as first argument
  • onEdict: invoked passing (id, state) as arguments.
  • onPoke: invoked passing (arg) an optional argument.

edict

Issuing an edict from a stateful Component.

param type required default explaination
id string yes Identifier of the component that is issuing an edict

getState

Safely get the current state from a registered component

param type required default explaination
id string yes Returns undefined if no Component or no sync method is found

poke

Poking Component passing optional argument.

param type required default explaination
id string yes Throw Error if the id component has no onPoke method
arg user-defined no undefined Optional argument to pass to onPoke

clearStore

clearStore will empty the bazar store. It's called without specifying arguments.

Contributing

Every PR is welcomed 🎉 If you have ideas on how to improve upon this library don't hesitate to email me at lucagesmundo@yahoo.it.

License

MIT.

Package Sidebar

Install

npm i bazar

Weekly Downloads

16

Version

0.5.2

License

MIT

Unpacked Size

36 kB

Total Files

12

Last publish

Collaborators

  • lucagez