One-to-One
andOne-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.
With NPM:
npm install bazar
Without:
<script src="https://unpkg.com/bazar@0.5.2/dist/bazar.js"></script>
-
Codepen demo showcasing
onPoke
: https://codepen.io/lucagez/full/GeRZeg -
Lots of snippets below.
- Some components (a), (b) express an
interest
in a registered component (c). - (c) updates its state and issue an
edict
. - (a) and (b) immediately receive the updated (c)'s state.
- A component (a) stores some info useful for the unreachable component (b).
- (a) can
poke
(b) passing the useful info.
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:
- Register some components.
- Dispatch events (either via
edict
orpoke
).
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>
}
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>
}
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>
);
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>
};
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>
};
In the following example:
- Registration of 3 components: (a), (b), (c).
- (a), (b) express an
interest
in a registered component (c). - (c) updates its state and issue an
edict
. - (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>
};
In the following example:
- Registration of 2 (unrelated) components: (a), (b).
- A component (a) stores some info useful for the unreachable component (b).
- (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>
};
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>
};
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();
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.
Issuing an edict
from a stateful Component.
param | type | required | default | explaination |
---|---|---|---|---|
id | string | yes | Identifier of the component that is issuing an edict |
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 |
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
will empty the bazar store. It's called without specifying arguments.
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
.
MIT.