This component intended to be used to synchronize same instances of it between each other using WebRTC (peer-to-peer) connection. Even though it's a peer-to-peer connection it still cannot work without a server of some sort. Server here is needed to establish a connection and let peers know about each other (this process is called signaling). After that, peers are sending data and messages directly to other peers, without involving the server.
It uses webrtc-swarm under the hood, which uses signalhub and simple-peer.
- Lock/Locking -- one component taking a control over all other components. The component taking a control is locking all other components. All other components are locked. When a component is locked is cannot send states to other components. Only one component at a time can be locking other components.
npm install webrtc-component
To run the server you need to install signalhub
globally (or locally and use ./node_modules/.bin/signalhub
):
npm install signalhub -g
# starts a signalhub server on 127.0.0.1:8080
signalhub listen -p 8080 -h 127.0.0.1
SyncComponent
can be used as a base class (instead of React.Component
) or as a standalone component instance inside another component.
import SyncComponent from 'webrtc-component';
class Counter extends SyncComponent {
static defaultProps = {
servers: ['http://192.168.1.50:4242'],
name: 'counter',
credentials: {}
}
constructor(...args) {
super(...args);
}
onStateReceived(state) {
this.setState(state);
}
render() {
return <div>...</div>
}
}
For more detailed example see examples/counter/index.js
Important not here is if you are using componentDidMount
or componentWillUnmount
in your component, then you need to call super.component*()
to make SyncComponent
work (because it uses other methods too). Example:
componentDidMount() {
super.componentDidMount();
// ... other things
}
class Counter extends SyncComponent {
constructor(...args) {
super(...args);
}
render() {
return <div>
<SyncComponent sync={{
servers: ['http://192.168.1.50:4242'],
name: 'counter',
credentials: {}
}} onStateReceived={(state) => {
this.setState(state);
}} />
</div>
}
}
No other ways are implemented yet. Though, if it will be needed, it can be possible to add a "Higher Order Component" composition way of using this component.
Configuration to the component is passed as a sync
prop (you may want to set it in defaultProps
) though. Example:
defaultProps = {
sync: {
// List of servers to use as "signaling servers".
// Servers must be `signalhub` servers
servers: ['...'],
// The name of this component's connection. `signalhub` server can handle
// many different connection from different components at the same time,
// so it's necessary to specify an unique name of your components' connection.
name: 'my-counter',
// If set, component automatically calls `sync.requestLock()` (editing mode)
// when `sync.setState()` is called. If timeout is set, locking will be
// canceled once it's passed. By default it's set and timeout is 5 seconds
autolock: { timeout: 5000 },
// Object with custom data. Specified data will be passed to the `onLockedBy` event
// of other (remote) components when one component starts updating its state.
// This allows to provide info and display "XYZ is editing"
credentials: {}
}
}
Instance of the Sync
class used internally by the component. All manipulations are performed with this object.
Sends state to other (remote) components. Doesn't affect the component being called on.
Typical you may want to call it right after this.setState()
, like this:
var newState = {
counter: this.state.counter
};
this.setState(newState);
this.sync.setState(newState)
Sends a lock request to other (remote) components. This request is not guaranteed to succeed. For example it may fail if 2 component send a request at the same time. In which case both requests may fail, or just one and the other one succeed. This depends on network state, speed and when requests where called.
The opposite of requestLock()
. When called, it cancels existing lock requests and unlocks remote components if lock was already performed.
Property which is equal to an unique id of the component locking current component, if any. If current component isn't locked, this property is null
.
True when lock was requested via requestLock()
but wasn't yet succeeded or failed.
This property is true when current component is locking other (remote) components.
Events are either passed as props
to an instance of SyncComponent
or placed on a component extending the SyncComponent
.
Called when state is received from other (remote) component.
Called when an other component established lock on the current component. credentials
data of the locking component are passed as data.credentials
.
Called when current component isn't being locked by any other component anymore.
Called when current component established locking over other (remote) components.
Called when current component stops locking other (remote) components.
Called when lock request from current component failed.
Called when other (remote) component (peer) is connected to the current component.
- First install the component if you didn't do that before
npm install
- Run
npm run counter:compile
- In a first console run
npm run hub
- In a second console run
npm run counter:server
- Open http://localhost:4141/ in your browser