目前,当socket.io从我的服务器发出'gmessage',并且我的组件中的套接字捕获它时,我的整个状态被替换。
所以当前的流程是这样的:
我从组件发出一条消息,它发送到我的服务器,然后发送到watson API。 API向我的服务器发送响应,服务器将该响应发送给组件。
因此,第一条消息创建与socket.io的连接,然后第二个socket.io事件被捕获在同一连接上。
有没有更好的方法来设置与socket.io的连接,并处理发射和“on gmessage”部分?感谢您提前提示。我还有新的反应,所以你看到我应该采取不同的做法是有帮助的!
...
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);
Fareed提供了一个可用的Redux解决方案,并建议对非Redux方法进行改进,因此我想解决当前代码中的问题:
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,
]
}))
}
由于你已经在使用redux,最好为你做socket.io dispatch redux动作,你可以使用与article描述相同的redux saga,或者你可以使用这个没有saga的实现,但你需要有redux-thunk中间件:
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} />
}
根据您的应用程序,我认为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([]);
...