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>
  );
}