当前,当socket.io从我的服务器发出“ gmessage”消息,并且组件中的套接字捕获了该消息时,整个状态将被替换。
所以当前的流是这样的:
我从组件发出一条消息,该消息到达我的服务器,然后到达Watson API。 API将响应发送到我的服务器,然后服务器将响应发送到组件。
因此,第一个消息创建了与socket.io的连接,然后第二个socket.io事件被捕获在同一连接上。
是否有更好的方法来建立与socket.io的连接,并同时处理发射和“消息传递”部分?感谢您提前任何提示。我仍然是新来的反应者,所以您发现我应该做不同的事情都是有帮助的!
...
import {Launcher} from 'react-chat-window'
import io from 'socket.io-client';
import { getUser } from '../actions/userActions';
class Workshop extends Component {
constructor() {
super();
this.state = {
messageList: []
};
}
_onMessageWasSent(message) {
this.setState({
messageList: [message, ...this.state.messageList]
})
var socket = io.connect('http://localhost:5000');
socket.emit('message', { message });
var myThis = this
var myState = this.state
socket.on('gmesssage', function (data) {
console.log(data);
myThis.setState({
messageList: [{
author: 'them',
type: 'text',
data: data
}, ...myState.messageList]
})
})
}
render() {
return (
<Grid container className="DashboardPage" justify="center">
<Grid item xs={12}>
<div>Welcome to your Workshop</div>
<TeamsTaskLists />
</Grid>
<Launcher
agentProfile={{
teamName: 'Geppetto',
imageUrl: 'https://geppetto.ai/wp-content/uploads/2019/03/geppetto-chat-avi.png'
}}
onMessageWasSent={this._onMessageWasSent.bind(this)}
messageList={this.state.messageList}
showEmoji
/>
</Grid>
);
}
}
const mapStatetoProps = state => ({
user: state.user
});
export default connect(
mapStatetoProps,
{ getUser }
)(Workshop);
答案 0 :(得分:3)
由于您已经在使用redux,所以最好让socket.io为您调度redux操作,因此可以使用与article描述相同的redux saga,也可以不使用saga来使用此实现,但是您需要使用{ {3}}中间件:
1-创建一个文件,说lib/socket.js
然后在此文件中,创建socket.io
客户端,使其称为socket
并创建initSocketIO
函数,您可以在其中触发任何redux动作以响应socket.io:
import socket_io from "socket.io-client";
// our socket.io client
export const socket = socket_io("http://localhost:5000");
/**
* init socket.io redux action
* @return {Function}
*/
export const initSocketIO = () => (dispatch, getState) => {
// connect
socket.on('connect', () => dispatch(appActions.socketConnected()));
// disconnect
socket.on('disconnect', () => dispatch(appActions.socketDisconnected()));
// message
socket.on('message', message => {
dispatch(conversationActions.addOrUpdateMessage(message.conversationId, message.id, message));
socket.emit("message-read", {conversationId: message.conversationId});
});
// message
socket.on('notifications', ({totalUnread, notification}) => {
dispatch(recentActions.addOrUpdateNotification(notification));
dispatch(recentActions.setTotalUnreadNotifications(totalUnread));
});
};
2-然后在App
挂载后在App
组件内部调用此操作:
import React, {Component} from "react";
import {initSocketIO} from "./lib/socket.js";
export class App extends Component {
constructor(props) {
super(props);
this.store = configureStore();
}
componentDidMount() {
// init socket io
this.store.dispatch(initSocketIO());
}
// ... whatever
}
3-现在,您还可以使用以下命令从任何组件触发任何socket.io操作:
import React, {Component} from "react";
import {socket} from "./lib/socket.js"
MyComponent extends Components {
myMethod = () => socket.emit("message",{text: "message"});
}
或通过任何使用thunk的redux操作,例如:
export const sendMessageAction = (text) => dispatch => {
socket.emit("my message",{text});
dispatch({type: "NEW_MESSAGE_SENT",payload:{text}});
}
注释:
1-这将完美地工作,但是我仍然喜欢使用redux-saga方法来管理API和socket.io等任何副作用。
2-在您的组件中,您无需将组件方法绑定到this
,只需使用箭头函数即可,例如:
myMethod = () => {
// you can use "this" here
}
render = (){
return <Launcher onMessageWasSent={this.myMethod} />
}
答案 1 :(得分:3)
Fareed提供了有效的Redux解决方案,并建议了非Redux方法的改进,因此,我想解决您当前代码中的问题:
您将在每次接收到新消息时重新初始化套接字变量并创建其侦听器,而不是一次在单独的文件中配置和初始化套接字对象,然后由Workshop组件以及可能的其他组件使用它整个项目中。
您要通过在 _onMessageWasSent 中调用setState({ messages: [...] })
两次来附加新接收到的消息。
要解决第一个问题,您可以创建一个单独的文件,并将与套接字相关的导入和初始化移至该文件,如下所示:
// socketHelper.js
import io from 'socket.io-client';
export const socket = io.connect('http://localhost:5000');
请记住,这是一个最小的配置,您可以在导出套接字对象之前对其进行调整,使其完全符合socket.io API允许的范围。
然后,在Workshop组件的 componentDidMount 中订阅该套接字对象,例如:
import { socket } from 'path/to/socketHelper.js';
class Workshop extends Component {
constructor(props) {
super(props);
...
}
componentDidMount() {
// we subscribe to the imported socket object just once
// by using arrow functions, no need to assign this to myThis
socket.on('gmesssage', (data) => {
this._onMessageWasSent(data);
})
}
...
}
如果您需要从远程端点加载数据,这是实例化网络请求的好地方。
componentDidMount 将只运行一次,因此您的监听器不会多次重新定义。
要解决第二个问题, _onMessageWasSent 处理函数应仅接收新消息,并使用setState将其追加到以前的消息中,如下所示:
_onMessageWasSent(data) {
this.setState(previousState => ({
messageList: [
{
author: 'them',
type: 'text',
data: data,
},
...previousState.messageList,
]
}))
}
答案 2 :(得分:1)
根据您的应用程序,我认为Redux在这方面可能会过大。
尽管要避免保存此引用,但我将在此处更改为lambda表达式:
socket.on('gmesssage', data => {
console.log(data);
this.setState({
messageList: [{
author: 'them',
type: 'text',
data: data
}, ...this.messageList]
})
})
您可能还会考虑使用新的React Hooks将其变成无状态组件并使用useState
:
const Workshop = () => {
const [messageList, setMessageList] = useState([]);
...