Set up Action Cable with Rails and React
Generate a new channel with bin/rails generate channel Conversations
and stream from a specific channel ID to broadcast conversations only to a specific inbox.
# app/channels/conversations_channel.rb
class ConversationsChannel < ApplicationCable::Channel
def subscribed
channel_id = "inbox-#{params[:inbox_slug]}"
stream_from channel_id
end
end
Broadcast to the channel ID with the desired object to return to the front-end.
# app/models/conversation.rb
class Conversation < ApplicationRecord
belongs_to :inbox
after_create_commit :broadcast_conversation
def broadcast_conversation
channel_id = "inbox-#{inbox.slug}"
ActionCable.server.broadcast(
channel_id,
{
id: id,
subject: subject
}
)
end
end
Set up a subscription in React to the same channel ID, specify the channel name and pass the expected inbox_slug params. When data is received the state will update and prepend the new conversation to the list.
import { createConsumer } from "@rails/actioncable";
import { useEffect, useState } from "react";
const CHANNEL_URL = process.env.NODE_ENV === "development" ? "ws://localhost:3000/cable" : "wss://example.com/cable";
const consumer = createConsumer(CHANNEL_URL);
const CONVERSATION_CHANNEL = "ConversationsChannel";
export default function Conversations({ conversations, inbox_slug }) {
const [conversationState, setConversationState] = useState(conversations);
useEffect(() => {
const subscription = consumer.subscriptions.create(
{ channel: CONVERSATION_CHANNEL, inbox_slug: inbox_slug },
{
received(data) {
setConversationState((prevState) => [{ ...data }, ...prevState]);
},
},
);
return () => subscription.unsubscribe();
}, []);
return (
<div>
<h1>Inbox</h1>
<ul>
{conversationState.map((conversation) => (
<li key={conversation.id}>
<div>{conversation.subject}</div>
</li>
))}
</ul>
</div>
);
}