質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Material-UI

Material-UIは、Material Designを利用可能なオープンソースのReact向けUIコンポーネントキットです。

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

1751閲覧

React + Pusherを使用したリアルタイム通信での問題

退会済みユーザー

退会済みユーザー

総合スコア0

Material-UI

Material-UIは、Material Designを利用可能なオープンソースのReact向けUIコンポーネントキットです。

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

2クリップ

投稿2020/02/11 21:52

編集2020/02/12 06:40

概要

現在、React + Materil-UI + Laravel +Pusherを使用したチャット機能の開発を行っています。
Material-UIのダイアログの中でチャットを実現したいと思っていまして、リアルタイム通信を実現するためにもPusherを使用しています。
ダイアログでチャットの送信画面を作ることはできました。また、送信後、Pusherのコンソールでにデータが送信されていることも確認済みです。
しかし、肝心のReact側でのリアルタイムの更新がうまくいきません…。
具体的には、messagesステートにデータを追加し、チャットのメッセージ部分を更新していこうと思っていたのですが、どうやらデータが帰ってきていないのか追加されていないようです。(console.logで確認しました。)
自分なりに色々調べてみましたが、解決せず…。
どなたか解決方法のご教授お願いいたします。

該当するコード

import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useChannel, useEvent } from 'use-pusher' import styled from 'styled-components' import Button from '@material-ui/core/Button' import IconButton from '@material-ui/core/IconButton' import Dialog from '@material-ui/core/Dialog' import DialogActions from '@material-ui/core/DialogActions' import DialogTitle from '@material-ui/core/DialogTitle' import Divider from '@material-ui/core/Divider' import TextsmsIcon from '@material-ui/icons/Textsms' import TelegramIcon from '@material-ui/icons/Telegram' import Chat from './../../../models/Chat' import User from './../../../models/User' import Board from './../../../models/Board' import Pusher from 'pusher-js' const ChatDialog = props => { const { open, onClose, fullScreen } = props const [msg, setMsg] = React.useState('') const [messages, setMessages] = React.useState([]) const userID = useSelector(state => state.UserReducer.userID) let isChanged = true useEffect(() => { // Pusher用のキーを設定 const pusher = new Pusher(process.env.MIX_PUSHER_APP_KEY, { cluster: process.env.MIX_PUSHER_APP_CLUSTER, encrypted: true }) // チャンネルの購読 const channel = pusher.subscribe('chat') channel.bind('ChatMessageReceived', data => { setMessages([...messages, data]) }) const token = JSON.parse(User.get('token')).token const boardID = Board.getBoardID() if (token) { Chat.get(token, boardID) .then(res => { setMessages(res.data.messages) }) .catch(e => { console.log(e) }) } }, [isChanged]) const handleOnChangeMsg = e => { setMsg(e.target.value) } const handleSendMsg = () => { const token = JSON.parse(User.get('token')).token const boardID = Board.getBoardID() if (token) { Chat.send(token, boardID, msg) .then(res => { setMsg('') }) .catch(e => { console.log(e) }) } } const messageList = ( Array.from(messages).map((msg, i) => ( <React.Fragment key={i}> {msg.user_id === userID ? <UserMsg>{msg.message}</UserMsg> : <Msg>{msg.message}</Msg>} </React.Fragment> )) ) const fullDialog = ( <Dialog fullScreen open={open} scroll="paper" aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > <Flex> <StyledDialogTitle><StyledTextsmsIcon /><Span>チャット</Span></StyledDialogTitle> <StyledDialogActions> <Button onClick={onClose} color="primary" autoFocus> <Span>閉じる</Span> </Button> </StyledDialogActions> </Flex> <DialogContent> <Textarea placeholder="メッセージを入力" onChange={handleOnChangeMsg} value={msg}></Textarea> <StyledIconButton onClick={handleSendMsg}><StyledTelegramIcon /></StyledIconButton> </DialogContent> <MessageArea> {messages.length === 0 ? <p>メッセージがありません</p> : messageList} </MessageArea> </Dialog > ) const nonFullDialog = ( <Dialog open={open} scroll="paper" fullWidth={true} maxWidth="sm" aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > <Flex> <StyledDialogTitle><StyledTextsmsIcon /><Span>チャット</Span></StyledDialogTitle> <StyledDialogActions> <Button onClick={onClose} color="primary" autoFocus> <Span>閉じる</Span> </Button> </StyledDialogActions> </Flex> <DialogContent> <Textarea placeholder="メッセージを入力" onChange={handleOnChangeMsg} value={msg}></Textarea> <StyledIconButton onClick={handleSendMsg}><StyledTelegramIcon /></StyledIconButton> </DialogContent> <MessageArea> {messages.length === 0 ? <p>メッセージがありません</p> : messageList} </MessageArea> </Dialog > ) return ( <> {fullScreen === true ? fullDialog : nonFullDialog} </> ) } const Flex = styled.div` display: flex; justify-content: space-between; background-color: #224272; ` const Textarea = styled.textarea` resize: none; width: 100%; height: 35px; border: 1px solid #224272; ` const Span = styled.span` font-size: 0.9em; color: white; ` const StyledDialogTitle = styled(DialogTitle)` color: white; ` const DialogContent = styled.div` height: 45px; display: flex; justify-content: space-between; padding: 0; padding-left: 5px; padding-top: 5px; ` const MessageArea = styled.div` height: 450px; ` const StyledDialogActions = styled(DialogActions)` ` const StyledIconButton = styled(IconButton)` vertical-align: middle; padding: 0 10px; height: 35px; ` const StyledTextsmsIcon = styled(TextsmsIcon)` vertical-align: middle; margin-right: 3px; ` const StyledTelegramIcon = styled(TelegramIcon)` vertical-align: middle; padding: 0; ` const Msg = styled.p` width: 100% text-align: left; ` const UserMsg = styled.p` width: 100% text-align: right; ` export default ChatDialog
// イベント ChatMessageReceived <?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class ChatMessageReceived implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $message; /** * Create a new event instance. * * @return void */ public function __construct($message) { $this->message = $message; } /** * Get the channels the event should broadcast on. * * @return \Illuminate\Broadcasting\Channel|array */ public function broadcastOn() { return new Channel('chat', $this->message); } }
// ChatController <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Events\ChatMessageReceived; use App\Models\Chat; use Auth; class ChatController extends Controller { public function index(Request $req) { $messages = Chat::where('board_id', $req->board_id)->orderBy('id', 'asc')->get(); return response()->json([ 'messages' => $messages->toArray() ]); } public function store(Request $req) { $user_id = Auth::guard('api')->user()->user_id; // データベースにメッセージを追加 $message = Chat::create([ 'board_id' => $req->board_id, 'user_id' => $user_id, 'message' => $req->message ]); event((new ChatMessageReceived($message))); return $message; } }

追記

下記の様に副作用(useEffect)のスキップ(isChangedで制御)を外したところ正常に動作いたしました。
しかし、今度はリクエストが永遠に送られつづけ、429 Too many requestsで死にます。
このことより、pusherとのデータのやりとりをする為のコードは間違っていないことがわかりました。
恐らく、useEffectが何度も走るということは、何度もレンダリング?されているからだと思うのですが、その原因がわかりません…。

  useEffect(() => { // Pusher用のキーを設定 const pusher = new Pusher(process.env.MIX_PUSHER_APP_KEY, { cluster: process.env.MIX_PUSHER_APP_CLUSTER, encrypted: true }) // チャンネルの購読 const channel = pusher.subscribe('chat') channel.bind('ChatMessageReceived', data => { console.log(data) setMessages([...messages, data]) console.log(messages) }) const token = JSON.parse(User.get('token')).token const boardID = Board.getBoardID() if (token) { Chat.get(token, boardID) .then(res => { setMessages(res.data.messages) }) .catch(e => { console.log(e) }) } })

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

useEffectの第2引数をmessagesに変更したらいけました。

投稿2020/02/14 14:42

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Vue+Laravel+Pusher
の組み合わせでしかやった事なくてReactよくわからんなんですが
Vue+Pusherでハマったのは

channel.bind('ChatMessageReceived', data => { setMessages([...messages, data]) })

この部分がレンダーループ?に入っていなくて、変数を更新しても反映されないって言う事がありました
いわゆるsetIntervalとかsetTimeout、ajax等の非同期で値個を更新したときと同じ症状です。
Reactはこの辺うまくやってくれるかもしれませんがご参考までに

投稿2020/02/12 05:04

mikkame

総合スコア5036

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/02/12 06:43

ご回答ありがとうございます! mikkameさんのご回答をきっかけにして試行錯誤し、その結果を追記いたしましたので、もし解決方法がお分かり頂く様であれば、ご回答いただけると幸いです。
mikkame

2020/02/12 07:05

Reactよくわかんないんですが Pusher用のキーを設定、チャンネルの購読 はUseEffectの外の方がいいのでは? それら初期設定はcomponentDidMount的なやつで描いた方がよさそう?
退会済みユーザー

退会済みユーザー

2020/02/12 09:19

なるほど。一応、useEffectがHooksと言われるcomponentDidMountに変わる処理になりますね。 もう一つ試したこととして、useEffectを2つ用意し、片方でpusherの初期設定を行っているんですが、だめですね…。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問