Use Rails 5+ ActionCable channels with React Native for realtime magic.
This is a fork from https://github.com/schneidmaster/action-cable-react
The react-native-action-cable
package exposes two modules: ActionCable, Cable.
ActionCable
: holds info and logic of connection and automatically tries to reconnect when connection is lost.Cable
: holds references to channels(subscriptions) created by action cable.
yarn add @kesha-antonov/react-native-action-cable
Import:
import {
ActionCable,
Cable,
} from '@kesha-antonov/react-native-action-cable'
Define once ActionCable and Cable in your application setup in your store (like Redux
or MobX
).
Create your consumer:
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable')
Right after that create Cable instance. It'll hold info of our channels.
const cable = new Cable({})
Then, you can subscribe to channel:
const channel = cable.setChannel(
`chat_${chatId}_${userId}`, // channel name to which we will pass data from Rails app with `stream_from`
actionCable.subscriptions.create({
channel: 'ChatChannel', // from Rails app app/channels/chat_channel.rb
chatId,
otherParams...
})
)
channel
.on( 'received', this.handleReceived )
.on( 'connected', this.handleConnected )
.on( 'rejected', this.handleDisconnected )
.on( 'disconnected', this.handleDisconnected )
...later we can remove event listeners and unsubscribe from channel:
const channelName = `chat_${chatId}_${userId}`
const channel = cable.channel(channelName)
if (channel) {
channel
.removeListener( 'received', this.handleReceived )
.removeListener( 'connected', this.handleConnected )
.removeListener( 'rejected', this.handleDisconnected )
.removeListener( 'disconnected', this.handleDisconnected )
channel.unsubscribe()
delete( cable.channels[channelName] )
}
You can combine React's lifecycle hooks componentDidMount
and componentWillUnmount
to subscribe and unsubscribe from channels. Or implement custom logic in your store
.
Here's example how you can handle events:
handleReceived = (data) => {
switch(data.type) {
'new_incoming_message': {
this.onNewMessage(data.message)
}
...
}
}
handleConnected = () => {
if (this.state.isWebsocketConnected) return
this.setState({ isWebsocketConnected: true })
}
handleDisconnected = () => {
if (!this.state.isWebsocketConnected) return
this.setState({ isWebsocketConnected: false })
}
Send message to Rails app:
cable.channel(channelName).perform('send_message', { text: 'Hey' })
cable.channel('NotificationsChannel').perform('appear')
ActionCable
top level methods:
.createConsumer(websocketUrl)
- create actionCable consumer and start connecting.startDebugging()
- start logging.stopDebugging()
- stop logging
ActionCable
instance methods:
.open()
- try connect.connection.isOpen()
- check ifconnected
.connection.isActive()
- check ifconnected
orconnecting
.subscriptions.create({ channel, otherParams... })
- create subscription to Rails app.disconnect()
- disconnects from Rails app
Cable
instance methods:
.setChannel(name, actionCable.subscriptions.create())
- set channel to get it later.channel(name)
- get channel by name
channel
methods:
.perform(action, data)
- send message to channel. action -string
, data -json
.on(eventName, eventListener)
- subscribe to events. eventName can bereceived
,connected
,rejected
,disconnected
.removeListener(eventName, eventListener)
- unsubscribe from event.unsubscribe()
- unsubscribe from channel
- Fork it ( https://github.com/kesha-antonov/react-native-action-cable/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
Obviously, this project is heavily indebted to the entire Rails team, and most of the code in lib/action_cable
is taken directly from Rails 5. This project also referenced fluxxor for implementation details and props binding.
MIT