React components for OpenTok.js. Unofficial fork of opentok/opentok-react.
- NodeJS
- Register a TokBox account: https://tokbox.com/account/user/signup
Add @doctrinab/opentok-react
as a dependency of your application:
npm install --save @doctrinab/opentok-react@1.0.0
Then include opentok.js
before your application:
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
Alternatively, wrap your top-level component using OpenTok with the preloadScript
HOC. The HOC will take care of loading opentok.js
for you before rendering.
There is an example application provided in example/
and you can run it with the following steps:
git clone https://github.com/opentok/opentok-react.git
cd opentok-react/example/
cp config.template.js config.js
- Edit
config.js
: - Add your OpenTok API key, Session ID and Token (https://tokbox.com/account/)
- Add your Chrome Extension ID (https://tokbox.com/developer/guides/screen-sharing/js/)
cd ..
yarn
(ornpm install
)npm run example
- Visit
http://localhost:8000
in your browser
Refer to the App.js
, Publisher.js
and Subscriber.js
files in example/components/
for library usage.
The following sections explains how to import and use opentok-react
in your React application.
Import the opentok-react
components into your React application:
import { OTSession, OTPublisher, OTStreams, OTSubscriber } from 'opentok-react';
Or require
it if you need to use CommonJS modules:
const { OTSession, OTPublisher, OTStreams, OTSubscriber } = require('opentok-react');
class MyApp extends React.Component {
render() {
return (
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTPublisher />
<OTStreams>
<OTSubscriber />
</OTStreams>
</OTSession>
);
}
}
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = { streams: [] };
}
componentWillMount() {
this.sessionHelper = createSession({
apiKey: 'your-api-key',
sessionId: 'your-session-id',
token: 'your-session-token',
onStreamsUpdated: streams => { this.setState({ streams }); }
});
}
componentWillUnmount() {
this.sessionHelper.disconnect();
}
render() {
return (
<div>
<OTPublisher session={this.sessionHelper.session} />
{this.state.streams.map(stream => {
return (
<OTSubscriber
key={stream.id}
session={this.sessionHelper.session}
stream={stream}
/>
);
})}
</div>
);
}
}
The opentok-react
library comprises of:
OTSession
ComponentOTPublisher
ComponentOTStreams
ComponentOTSubscriber
ComponentcreateSession
HelperpreloadScript
Higher-Order Component
Prop | Type | Required | Description |
---|---|---|---|
apiKey | String | Yes | TokBox API Key |
sessionId | String | Yes | TokBox Session ID |
token | String | Yes | TokBox token |
options | Object | No | TokBox options options |
eventHandlers | Object<Function> | No | Event handlers passed into session.on |
onConnect | Function() | No | Invoked when session.connect successfully completes |
onError | Function(err) | No | Invoked when session.connect fails |
The OTSession
component manages the connection to an OpenTok Session. It passes the Session instance as the session
prop to its child components. It is recommended that you nest OTPublisher
and OTStreams
inside OTSession
:
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTPublisher />
<OTStreams>
<OTSubscriber />
</OTStreams>
</OTSession>
Prop | Type | Required | Description |
---|---|---|---|
session | Session | No | OpenTok Session instance. This is auto populated by wrapping OTPublisher with OTSession |
properties | Object | No | Properties passed into OT.initPublisher |
eventHandlers | Object<Function> | No | Event handlers passed into publisher.on |
onInit | Function() | No | Invoked when OT.initPublisher successfully completes |
onPublish | Function() | No | Invoked when session.publish successfully completes |
onError | Function(err) | No | Invoked when either OT.initPublisher or session.publish fail |
The OTPublisher
component will initialise a publisher and publish to a specified session upon mounting. It will also ensure the Publisher video element is attached to the DOM inside the component rather than appending to the body (which is the Publisher's default behaviour).
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTPublisher />
</OTSession>
If you are not using OTSession
then you must specify a Session object using the session
prop.
<OTPublisher session={this.sessionHelper.session} />
Use the properties
prop to specify a properties object for OT.initPublisher and the eventHandlers
prop to specify an object of event handlers for Publisher#on.
class MyApp extends React.Component {
constructor(props) {
super(props);
this.publisherProperties = {
audioFallbackEnabled: false,
showControls: false
};
this.publisherEventHandlers = {
streamCreated: event => {
console.log('Publisher stream created!');
},
streamDestroyed: event => {
console.log('Publisher stream destroyed!');
}
};
}
render() {
return (
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTPublisher
properties={this.publisherProperties}
eventHandlers={this.publisherEventHandlers}
/>
</OTSession>
);
}
}
The properties
prop is used for initial set up of the Publisher and making changes to it will not update the Publisher, you will instead need to invoke Publisher methods. To facilitate this the Publisher instance is exposed via the getPublisher()
component method. You should always call this method to get the latest Publisher instance instead of keeping a reference to it as it's possible for the Publisher instance to change, leaving you with a stale reference.
However, for convenience the OTPublisher
does watch for changes on a few keys of the properties
object and makes the necessary changes. Currently these are:
Publisher Property | Action |
---|---|
videoSource | Destroys and recreates the Publisher with the new video source. This is the only way to change the video source of a Publisher. This is used in the example application to allow the user to toggle between publishing their camera and publishing their screen |
publishAudio | Calls publisher.publishAudio() to toggle audio on and off |
publishVideo | Calls publisher.publishVideo() to toggle video on and off |
There are plans to support more Publisher properties but for now you will have to call getPublisher()
to retrieve the Publisher instance and make the necessary changes yourself.
You can also get access to the publisher
object by calling the getPublisher
method using a Ref. For example:
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
publisher: null,
};
this.otPublisher = React.createRef();
}
componentDidMount() {
this.getPublisher();
}
getPublisher() {
if (this.otPublisher) {
this.setState({
publisher: this.otPublisher.current.getPublisher(),
});
}
}
render() {
return (
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTPublisher
ref={this.otPublisher}
/>
</OTSession>
);
}
}
Prop | Type | Required | Description |
---|---|---|---|
children | ReactElement | Yes | Must have a single child component that accepts session and stream props, eg. OTSubscriber |
session | Session | Yes | OpenTok Session instance. This is auto populated by wrapping OTStreams with OTSession |
streams | Array<Stream> | No | Array of OpenTok Stream instances. This is auto populated by wrapping OTStreams with OTSession |
Prop | Type | Required | Description |
---|---|---|---|
session | Session | No | OpenTok Session instance. This is auto populated by wrapping OTSubscriber with OTStreams |
stream | Stream | No | OpenTok Stream instance. This is auto populated by wrapping OTSubscriber with OTStreams |
properties | Object | No | Properties passed into session.subscribe |
retry | Boolean | No | Set true to retry the subscribe process in case of failure (Default: false) |
maxRetryAttempts | Number | No | Max retry attempts in case of subscribe failure (Default: 5) |
retryAttemptTimeout | Number | No | Timeout value between every subscribe retry attempt, expressed in ms (Default: 1000ms) |
eventHandlers | Object<Function> | No | Event handlers passed into subscriber.on |
onSubscribe | Function() | No | Invoked when session.subscribe successfully completes |
onError | Function(err) | No | Invoked when session.subscribe fails |
The OTSubscriber
component will subscribe to a specified stream from a specified session upon mounting. It will also ensure the Subscriber video element is attached to the DOM inside the component rather than appending to the body (which is the Subscriber's default behaviour).
<OTStreams>
<OTSubscriber />
</OTStreams>
If you are not using OTStreams
then you must provide a Stream object using the stream
prop and a Session object using the session
prop.
{this.sessionHelper.streams.map(stream => (
<OTSubscriber
key={stream.id}
session={this.sessionHelper.session}
stream={stream}
/>
))}
Use the properties
prop to specify a properties object for session.subscribe and the eventHandlers
prop to specify an object of event handlers for Subscriber#on.
class MyApp extends React.Component {
constructor(props) {
super(props);
this.subscriberProperties = {
preferredFrameRate: 15,
showControls: false
};
this.subscriberEventHandlers = {
videoDisabled: event => {
console.log('Subscriber video disabled!');
},
videoEnabled: event => {
console.log('Subscriber video enabled!');
}
};
}
render() {
return (
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTStreams>
<OTSubscriber
properties={this.subscriberProperties}
eventHandlers={this.subscriberEventHandlers}
/>
</OTStreams>
</OTSession>
);
}
}
The properties
prop is used for initial set up of the Subscriber and making changes to it will not update the Subscriber, you will instead need to invoke Subscriber methods. To facilitate this the Subscriber instance is exposed via the getSubscriber()
component method. You should always call this method to get the latest Subscriber instance instead of keeping a reference to it as it's possible for the Subscriber instance to change, leaving you with a stale reference.
However, for convenience the OTSubscriber
does watch for changes on a few keys of the properties
object and makes the necessary changes. Currently these are:
Subscriber Property | Action |
---|---|
subscribeToAudio | Calls subscriber.subscribeToAudio() to toggle audio on and off |
subscribeToVideo | Calls subscriber.subscribeToVideo() to toggle video on and off |
There are plans to support more Subscriber properties but for now you will have to call getSubscriber()
to retrieve the Subscriber instance and make the necessary changes yourself.
You can also get access to the subscriber
object by calling the getSubscriber
method using a Ref. For example:
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
subscriber: null,
};
this.otSubscriber = React.createRef();
}
componentDidMount() {
this.getSubscriber();
}
getSubscriber() {
if (this.otSubscriber) {
this.setState({
subscriber: this.otSubscriber.current.getSubscriber(),
});
}
}
render() {
return (
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
<OTStreams>
<OTSubscriber
ref={this.otSubscriber}
/>
</OTStreams>
</OTSession>
);
}
}
The createSession
helper has been provided to easily create a session and monitor the current list of subscriber streams.
class MyApp extends React.Component {
componentWillMount() {
this.sessionHelper = createSession({
apiKey: 'your-api-key',
sessionId: 'your-session-id',
token: 'your-session-token',
onStreamsUpdated: streams => {
console.log('Current subscriber streams:', streams);
}
});
}
componentWillUnmount() {
this.sessionHelper.disconnect();
}
}
The createSession
helper returns an object with the following properties:
session
- The Session instance.streams
- An up-to-date array of Stream instances.disconnect
- A clean up function. Call this when your component unmounts.
Use of this helper is optional and you can instead use the OTSession
component or directly call OT.initSession and listen to streamCreated events if you prefer.
Prop | Type | Required | Description |
---|---|---|---|
opentokClientUrl | String | No | The URL of the OpenTok client script to load. It defaults to https://static.opentok.com/v2/js/opentok.min.js . |
loadingDelegate | ReactElement | No | An element that will be displayed while the OpenTok client script is loading. It defaults to an empty <div /> . |
In larger applications, one might not want to load the opentok.js
client with a <script>
tag all the time. The preloadScript
higher-order component will do this for you at the appropriate time.
For example, imagine you have a React Router application with the following route structure:
<Router>
<Route path="/">
<IndexRoute component="..." />
<Route path="something" component="..." />
<Route path="video" component={VideoChat} />
<Route path="something-else" component="..." />
</Route>
</Router>
What you'd like to do is delay the loading of opentok.js
until the VideoChat
component is being used. Here's how you can do this:
class VideoChat extends React.Component {
// All the code of your component
}
export default preloadScript(App);
git clone https://github.com/opentok/opentok-react.git
cd opentok-react/
yarn
(ornpm install
)- Modify code in
src/
npm run build
- Check that files in
dist/
have been updated.
Run the unit tests locally with the following command:
npm run unit
By default this will launch the Chrome browser in headless mode. To run tests in Chrome use:
npm run unit -- --browsers Chrome
Run the linter with:
npm run lint
The unit tests are automatically run on Github Actions in Chrome Headless and the current build status is shown at the top of this document.
Originally authored by Aiham Hammami. Please note that this is not officially supported by TokBox.