Use Rails ActionCable channels with React Native for real-time WebSocket communication.
- π WebSocket Connection - Automatic connection management with reconnection support
- π‘ Channel Subscriptions - Subscribe to multiple ActionCable channels
- π Auto-Reconnect - Automatically reconnects when connection is lost
- π Custom Headers - Support for authentication and dynamic headers
- π± React Native Ready - Works without
windowobject polyfills - π‘οΈ Connection Reuse - Prevent duplicate connections during hot reloads
- β‘ TypeScript - Full TypeScript support included
- β¨ Features
- π Table of Contents
- π¦ Installation
- π Quick Start
- π API Reference
- βοΈ Advanced Usage
- π§ͺ Testing
- π Examples
- π€ Contributing
- π Credits
- π License
Yarn
yarn add @kesha-antonov/react-native-action-cablenpm
npm install @kesha-antonov/react-native-action-cableimport { ActionCable, Cable } from '@kesha-antonov/react-native-action-cable'
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable')
const cable = new Cable({})const channel = cable.setChannel(
'ChatChannel',
actionCable.subscriptions.create({
channel: 'ChatChannel',
roomId: 1
})
)
channel
.on('received', (data) => console.log('Received:', data))
.on('connected', () => console.log('Connected!'))
.on('disconnected', () => console.log('Disconnected'))channel.perform('send_message', { text: 'Hello!' })channel.unsubscribe()| Method | Description |
|---|---|
createConsumer(url, headers?) |
Create a new consumer and connect |
getOrCreateConsumer(url, headers?) |
Reuse existing consumer or create new one |
disconnectConsumer(url) |
Disconnect and remove consumer from cache |
startDebugging() |
Enable debug logging |
stopDebugging() |
Disable debug logging |
| Method | Description |
|---|---|
subscriptions.create(params) |
Create a channel subscription |
connection.isOpen() |
Check if connected |
connection.isActive() |
Check if connected or connecting |
disconnect() |
Disconnect from server |
| Method | Description |
|---|---|
setChannel(name, subscription) |
Register a channel |
channel(name) |
Get channel by name |
| Method | Description |
|---|---|
on(event, callback) |
Subscribe to events: received, connected, disconnected, rejected, error |
removeListener(event, callback) |
Remove event listener |
perform(action, data) |
Send message to server |
unsubscribe() |
Unsubscribe from channel |
Custom Headers & Authentication
// Static headers
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable', {
'Authorization': 'Bearer token123'
})
// Dynamic headers (re-evaluated on each connection)
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable', () => ({
'Authorization': `Bearer ${getAuthToken()}`
}))Preventing Duplicate Connections
Use getOrCreateConsumer to prevent duplicate connections during hot reloads:
// β Creates new connection every time
const actionCable = ActionCable.createConsumer('ws://localhost:3000/cable')
// β
Reuses existing connection
const actionCable = ActionCable.getOrCreateConsumer('ws://localhost:3000/cable')Error Handling
channel.on('error', (error) => {
console.log('Connection error:', error)
// Handle: no internet, wrong URL, server down, auth failure
})React Hook Example
function useActionCable(channelName: string, params: Record<string, unknown>) {
const [connected, setConnected] = useState(false)
useEffect(() => {
const channel = cable.setChannel(
channelName,
actionCable.subscriptions.create({ channel: channelName, ...params })
)
channel
.on('connected', () => setConnected(true))
.on('disconnected', () => setConnected(false))
.on('received', handleReceived)
return () => {
channel.removeListener('received', handleReceived)
channel.unsubscribe()
delete cable.channels[channelName]
}
}, [channelName])
return { connected, channel: cable.channel(channelName) }
}Custom Action Events
Messages with data.action attribute are emitted as separate events:
# Rails sends:
{ action: 'speak', text: 'hello!' }// React Native receives:
channel.on('speak', (data) => {
console.log(data.text) // 'hello!'
})jest.mock('@kesha-antonov/react-native-action-cable', () => ({
ActionCable: {
createConsumer: jest.fn(() => ({
subscriptions: {
create: jest.fn(() => ({
on: jest.fn().mockReturnThis(),
removeListener: jest.fn().mockReturnThis(),
perform: jest.fn(),
unsubscribe: jest.fn(),
})),
},
connection: {
isActive: jest.fn(() => true),
isOpen: jest.fn(() => true),
},
disconnect: jest.fn(),
})),
},
Cable: jest.fn(() => ({
channels: {},
channel: jest.fn(),
setChannel: jest.fn(),
})),
}))See examples/testing for complete testing examples.
| Example | Description |
|---|---|
| Complete Chat App | Full Rails backend + React Native frontend |
| Apollo GraphQL | ActionCable with GraphQL subscriptions |
| Testing | Jest mocks and testing patterns |
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Based on action-cable-react. Code in lib/action_cable is adapted from Rails ActionCable.
Please note that this project is maintained in free time. If you find it helpful, please consider becoming a sponsor.