Skip to content

Instantly share code, notes, and snippets.

@jevin
Created July 30, 2019 11:56
Show Gist options
  • Save jevin/414dcdb1b1fe6d5e48c2e9dc53fbc931 to your computer and use it in GitHub Desktop.
Save jevin/414dcdb1b1fe6d5e48c2e9dc53fbc931 to your computer and use it in GitHub Desktop.
Using ActionCable in Expo/React Native
# config/routes.rb
Rails.application.routes.draw do
mount ActionCable.server, at: '/cable'
end
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_channel"
end
def unsubscribed
end
def speak(data)
ActionCable.server.broadcast("chat_channel", message: "#{data["message"]}")
end
end
# Goes inside componentDidMount() in any screen
this.ws = new WebSocket('ws://0.0.0.0:3000/cable');
this.channel_name = 'ChatChannel';
this._handleMessage = (data) => {
this.setState({
messages: this.state.messages.concat([data.message.message])
})
}
this.ws.onopen = () => {
const msg = {
command: 'subscribe',
identifier: JSON.stringify({
channel: this.channel_name,
}),
};
this.ws.send(JSON.stringify(msg));
};
this.ws.onmessage = e => {
var data = JSON.parse(e.data);
if (data.identifier != undefined) {
data.identifier = JSON.parse(data.identifier)
}
if (data.type == "welcome" || data.type == "ping" || data.type == "confirm_subscription") {
return;
}
if (data.identifier.channel == this.channel_name) {
this._handleMessage(data)
return;
}
};
this.ws.onerror = e => {
console.log("ERROR: " + e.message);
};
this.ws.onclose = e => {
console.log("CONNECTION CLOSED: " + e.code + " " + e.reason);
};
this.ws.sendmessage = data => {
const msg = {
command: 'message',
identifier: JSON.stringify({
channel: 'ChatChannel',
}),
data: JSON.stringify(data)
};
this.ws.send(JSON.stringify(msg));
}
# Somewhere in the code
_sendMessage = (message) => {
this.ws.sendmessage({
action: 'speak',
message: message,
});
}
<TouchableOpacity onPress={() => this._sendMessage("Hello world!)}>
<Text>
Send message
</Text>
</TouchableOpacity>
@misraelson
Copy link

Would you set a this.ws.close() inside your componentWillUnmount() and send a message to 'unsubscribe' in the cable?

@jevin
Copy link
Author

jevin commented Jul 22, 2020

Thanks! The whole code needs a refresh. I think you’re right in your this.ws.close() placement.

@jevin
Copy link
Author

jevin commented Jul 22, 2020

Thanks! The whole code needs a refresh. I think you’re right in your this.ws.close() placement.

@misraelson
Copy link

Thanks! The whole code needs a refresh. I think you’re right in your this.ws.close() placement.

Is there anything else you need to do on the rails side to channel.rb or connection.rb?

@misraelson
Copy link

I guess our ChatChannel inherits from Channel < ActionCable::Channel::Base so that sort of takes care of it. I like this approach instaed of using the action cable npm package on the front end which seems a bit more work than its worth. cheers!

@jevin
Copy link
Author

jevin commented Jul 22, 2020

Yeah I don’t think anything else is needed. The idea was to make this Gist as simple as possible.

Like you said, that’s why I’m not using any npm package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment