WebSocketsはRails、RUDX、およびRubyでRailsで動作します
注意:この投稿のコードはすべて見つかります.here(frontend) and here (backend/rails)
Rails ActionWire機能(WebSockets)を統合して、ReduxとRedux(Reduxツールキットを通して)を使っている基本的なチャット・アプリケーションで、方法を調査しましょう.私はコードの最も関連したスニペットだけを含んでいますrepo コンテキスト全体.
これは私たちが構築するものです.
バックエンド
私はAPIのエンドポイントとしてRailsを使用しているので、私は
我々のアプリは、単にユーザーとメッセージがあります.そのモデルを作りましょう
最後に、我々は
フロントエンド
フロントエンドのために、私はReduxとTypesScriptで反応を使用しています.アプリを作成しましょう:
を作成して起動します
フロントエンドの非同期呼び出しで動作する場合、通常は非同期呼び出しを行い、成功した場合、アプリケーションの状態を更新します.thunksで、プロセスは同じです、しかし、すべては還元器自体で扱われます.我々は、アクションを派遣する必要があります後に実行される(例えば、成功した呼び出し)し、状態を更新します.
これは、新しいユーザを追加し、メッセージを送信するために、thunkがどのように見えるかです.
レールを呼び出す
すべての構成とスタイルで、これは全体のチャットアプリケーションのようになります:
それを使用すると、我々は、反応、redux、およびRailsとWebSocketsを使用してアプリケーションがあります.
Rails ActionWire機能(WebSockets)を統合して、ReduxとRedux(Reduxツールキットを通して)を使っている基本的なチャット・アプリケーションで、方法を調査しましょう.私はコードの最も関連したスニペットだけを含んでいますrepo コンテキスト全体.
これは私たちが構築するものです.
バックエンド
私はAPIのエンドポイントとしてRailsを使用しているので、私は
--api
フラグ.これは、我々が何かを呼ぶとき、ビューが発生するのを防ぎますrails generate
したがって、不要なコードを避けるコマンドです.さらに、我々は使用しますpostgresql
DBとして.rails new chat-app-backend-rails --api -database=postgresql
私たちの独立した独立したプロジェクトとして我々のフロントエンドを構築しているので、潜在的に我々のAPIより異なるサーバーに配備されて、我々はクロスドメイン呼び出しを考慮に入れる必要があります.そのためには、まずrack-cors
でGemfile
:gem 'rack-cors'
そして、config/initializers/cors.rb
.Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
# In a prod app you'll restrict to specific origin(s).
# for this will just allow from any.
origins '*'
resource '*',
headers: :any,
methods: %i[get post put patch delete options head]
end
end
我々bundle install
我々は追加宝石をインストールします.我々のアプリは、単にユーザーとメッセージがあります.そのモデルを作りましょう
rails generate model User
rails generate model Message
我々User
のみusername
and status
これは移行の様子です.class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :username
t.string :status
t.timestamps
end
end
end
とMessage
:class CreateMessages < ActiveRecord::Migration[7.0]
def change
create_table :messages do |t|
t.string :content
t.timestamps
end
end
end
我々のモデルは1-to-many
関係(1ユーザーは多くのメッセージがあります).我々はそれを追加することによってキャプチャされますhas_many :messages
でUser
and belongs_to
on Message
.class User < ApplicationRecord
has_many :messages, dependent: :destroy
end
class Message < ApplicationRecord
belongs_to :user
end
最後に、リファレンスを追加するマイグレーションを追加しますuser_id
) メッセージに.rails generate migration AddBelongToMessages
このコードでclass AddBelongToMessages < ActiveRecord::Migration[7.0]
def change
add_belongs_to :messages, :user
end
end
注:我々は最初に作成したときに我々はこれを追加することができますMessage
移行最後に、我々は
migrate
コマンドrails db:migrate
次に、使用するすべてのルートを追加し、ActionCable ( WebSocket )サーバーをマウントしましょう. resources :messages, only: %i[index]
resources :users, only: %i[index create] do
post 'add_message'
post 'change_status'
end
mount ActionCable.server => '/cable'
これはセットアップのためです.我々は今、いくつかの機能を追加を開始する準備が整いました.の作成を開始しましょうmessages
and users
チャンネル.我々は、チャットの上で掲示されるメッセージのために、そして、参加しているユーザーのために、これらを使用します.rails generate channel messages
rails generate channel users
両方の生成されたチャンネルでは、我々は単にsubscribed
ストリーミングの場所を指定する方法class MessagesChannel < ApplicationCable::Channel
def subscribed
stream_from 'message_channel'
end
def unsubscribed; end
end
class UsersChannel < ApplicationCable::Channel
def subscribed
stream_from 'user_channel'
end
def unsubscribed; end
end
今、我々はActionCable.server.broadcast()
これらのチャネル上のすべての加入者にブロードキャストする方法.我々は、すべての加入者に通知したいuser_channel
ユーザーがチャットに参加したとき.また、通知したいmessage_channel
メッセージの送信後.両方のことをしましょうUsersController
:class UsersController < ApplicationController
def index
users = User.all
render json: users
end
def create
user = User.new(user_params)
ActionCable.server.broadcast('user_channel', user) if user.save
render json: user
end
def add_message
user = User.find(params[:user_id])
message = params[:message]
created_message = user.messages.create(content: message)
ActionCable.server.broadcast('message_channel', created_message) if user.save
head :ok
end
def change_status; end
def user_params
params.require(:user).permit(:username, :status)
end
end
完成のために、私たちもMessagesController
これは、チャットに参加したユーザーのためのすべてのメッセージを返します.class MessagesController < ApplicationController
def index
messages = Message.all
render json: messages
end
end
これにより、フロントエンドとの統合を必要とするAPIコールがすべてあります.rails routes | grep users
user_add_message POST /users/:user_id/add_message(.:format)
user_change_status POST /users/:user_id/change_status(.:format)
users GET /users(.:format)
POST /users(.:format) users#create
rails routes | grep messages
messages GET /messages(.:format)
フロントエンド
フロントエンドのために、私はReduxとTypesScriptで反応を使用しています.アプリを作成しましょう:
npx create-react-app chat-app-ui --template redux-typescript
このテンプレートでは、ツールキットを使用してReduxを使用するアプリケーションスケルトンを提供します.を作成して起動します
/features/users
フォルダ.そこにすべてを追加しますapi
and reducer
機能.そこで私はusersAPI
ユーザーに関連するすべてのバックエンド呼び出しで.例えば、チャットに新しいユーザを追加する方法です.export const addNewUser = async (user: UserType): Promise<any> => {
const res = await fetch("http://localhost:3090/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user),
});
return await res.json();
};
そして、これはどのように我々はメッセージを送るユーザーを扱いますかexport const sendUserMessage = async (
data: sendUserMessageDataType
): Promise<any> => {
const res = await fetch(
`http://localhost:3090/users/${data.user.id}/add_message`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: data.user.id,
message: data.message.content,
}),
}
);
return await res.json();
};
我々はこれらのAPIを間接的にredux thunksを介して呼び出しを使用します.フロントエンドの非同期呼び出しで動作する場合、通常は非同期呼び出しを行い、成功した場合、アプリケーションの状態を更新します.thunksで、プロセスは同じです、しかし、すべては還元器自体で扱われます.我々は、アクションを派遣する必要があります後に実行される(例えば、成功した呼び出し)し、状態を更新します.
これは、新しいユーザを追加し、メッセージを送信するために、thunkがどのように見えるかです.
...
export const addUserAsync = createAsyncThunk(
'users/addUser',
async (user: UserType) => {
const response = await addNewUser(user);
return response;
}
)
export const sendMessageAsync = createAsyncThunk(
'users/sendMessage',
async (data: sendUserMessageDataType) => {
const response = await sendUserMessage(data);
return response;
}
)
...
次に、extraReducers
セクションcreateSlice()
....
extraReducers: (builder) => {
builder
.addCase(sendMessageAsync.fulfilled, (state, action) => {
let updatedUser: UserType = state.value.filter(user => user.id === action.payload.user.id)[0];
updatedUser.messages.push(action.payload.message);
state.value = state.value.map(user => user.id !== updatedUser.id ? user : updatedUser)
})
.addCase(addUserAsync.fulfilled, (state, action) => {
state.value.push(action.payload);
localStorage.setItem("currentUser", JSON.stringify(action.payload));
state.userLoggedIn = true;
})
},
...
あなたは全体の減速を確認することができますhere .レールを呼び出す
ActionCable
我々は、インストールする必要がありますactioncable
パッケージ.npm install --save actioncable
これが我々の使用方法ですactioncable
にMessages.tsx
新しいメッセージを購読するには、import { useAppDispatch, useAppSelector } from "../app/hooks";
import { addMessage, selectMessages } from "../features/messages/messagesSlice";
import { MessageType } from "../types";
import Message from "./Message";
import ActionCable from "actioncable";
import { useEffect } from "react";
function Messages() {
const messages: MessageType[] = useAppSelector(selectMessages);
const cable = ActionCable.createConsumer("ws://localhost:3090/cable");
const dispatch = useAppDispatch();
const createSubscription = () => {
cable.subscriptions.create(
{ channel: "MessagesChannel" },
{ received: (message) => handleReceivedMessage(message) }
);
};
const handleReceivedMessage = (message: any) => {
dispatch(addMessage(message));
};
useEffect(() => {
createSubscription();
}, []);
return (
<div className="">
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
);
}
export default Messages;
我々は、同じアプローチを使用しますUsers.tsx チャットに参加する新しいユーザーを購読する.すべての構成とスタイルで、これは全体のチャットアプリケーションのようになります:
それを使用すると、我々は、反応、redux、およびRailsとWebSocketsを使用してアプリケーションがあります.
Reference
この問題について(WebSocketsはRails、RUDX、およびRubyでRailsで動作します), 我々は、より多くの情報をここで見つけました https://dev.to/fndomendez/websockets-with-react-redux-and-ruby-on-rails-18hbテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol