In this post I will show how to use RxJs to implement pub sub in React components.
RxJs makes implementing pub sub a breeze! In my view it’s one of the major strengths of the framework.
In my following example I will show how to wire this up between two React components.
The sample application is pretty straightforward. I have implemented two greeting components - one component for creating greetings, and a second component to display the greetings.
The bridge that connects the two is an RxJS pub sub channel.
The actual channel is implemented using an RxJs Subject, but I am wrapping this in a service called GreetingService.
greeting-service.js
import { Subject } from 'rxjs/Subject';
export default class GreetingService {
constructor() {
this.subject = new Subject();
}
emit(value) {
this.subject.next(value);
}
greetings() {
return this.subject.asObservable();
}
}
The greetings method exposes the Observable part of the Subject. There is also an emit method that can be used to emit new greetings as they are produced.
The components are pretty straightforward:
create-greeting.js
import React, { Component } from 'react';
class CreateGreeting extends Component {
constructor(props) {
super();
this.state = {value: ''};
this.greetingService = props.greetingService;
this.handleChange = this.handleChange.bind(this);
this.sendGreeting = this.sendGreeting.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
sendGreeting() {
this.greetingService.emit(this.state.value);
}
render() {
return (
<div>
<h4>Create Greeting</h4>
<input type="text" onChange={this.handleChange} />
<button onClick={this.sendGreeting}>Submit</button>
</div>
);
}
}
export default CreateGreeting;
display-greeting.js
import React, { Component } from 'react';
class DisplayGreeting extends Component {
constructor(props) {
super();
this.state = {greeting: ''};
this.stopGreetings = this.stopGreetings.bind(this);
this.greetingService = props.greetingService;
this.subscription = this.greetingService
.greetings()
.subscribe(res => {
this.setState({greeting: res});
});
}
render() {
let greeting = this.state.greeting;
return (
<div>
<h4>Display Greeting</h4>
<div>{greeting}</div>
<button onClick={this.stopGreetings}>Stop</button>
</div>
);
}
stopGreetings() {
this.subscription.unsubscribe();
}
}
export default DisplayGreeting;
I guess there are different ways to pass the GreetingService to the components. In this sample I am simply passing it in as a prop.
The important part here is that both components refer to the same instance of the service. If you have a more elegant way to share the service, great! I would love to hear about it.
The two greeting components can then be added to the root component template like so:
app.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import GreetingService from './pub-sub/greeting-service';
import DisplayGreeting from './pub-sub/display-greeting';
import CreateGreeting from './pub-sub/create-greeting';
let greetingService = new GreetingService();
class App extends Component {
constructor() {
super();
}
render() {
return (
<div className="App">
<h3>Pub Sub Demo</h3>
<CreateGreeting greetingService={greetingService} />
<DisplayGreeting greetingService={greetingService} />
</div>
);
}
}
export default App;
Now that the components have a handle to the service, I can start flowing greetings through the channel.
On button click I call “emit” on the service from the CreateGreeting component.
The greeting is instantly reflected in the DisplayGreeting component via the GreetingService subscription. It's important to remember to subscribe to the Observable. Otherwise you won't see any messages coming through the pub sub channel.
As soon as you are tired of receiving greetings, you can unsubscribe from the channel and terminate the flow of greetings.
I have put the code on Github in case you are interested in checking it out.