如何解决Websockets-每当从Node WS服务器发送消息时,都会导致所有React客户端刷新
我为此苦苦挣扎太久了。我有一台带有websockets的Node服务器,它可以与React客户端之间收发消息。但是由于某种原因,每当从Node发送websocket消息时,都会导致所有React客户端刷新其顶级组件。因此,如果某人正处于某个客户的中间,就会使他们陷入混乱。我什至不知道该如何判断我的Node代码或React代码还是这两个问题。
React代码很大,如果不需要,我不想掉进一个兔子洞。因此,我将在Node服务器上发布websocket代码,希望有人能在其中找到一些东西。您也可以告诉我是否需要查看一些React代码。但我希望这仅仅是Node WS问题。
setupWebSocket.js
// setupWebSocket.js
import WebSocket from 'ws';
import { broadcastPipeline } from './pipeline.js';
import DeviceDetector from 'device-detector-js';
const loginAccounts = [
{ pw: '12345',first: 'First',last: 'Last' },];
let activeLogins = [];
export const setupWebSocket = (server) => {
// ws instance
const wss = new WebSocket.Server({ noServer: true });
//handle upgrade of the request
server.on('upgrade',function upgrade(request,socket,head) {
try {
// authentication and some other steps will come here
// we can choose whether to upgrade or not
wss.handleUpgrade(request,head,function done(ws) {
wss.emit('connection',ws,request);
});
} catch (err) {
console.log('upgrade exception',err);
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
}
});
const deviceDetector = new DeviceDetector();
// what to do after a connection is established
wss.on('connection',(ctx,req) => {
const userAgent = req.headers['user-agent'];
const device = deviceDetector.parse(userAgent);
// print number of active connections
console.log('Client connected!');
console.log('connected ',wss.clients.size);
// if it is an ipad,save that info with the client
if (device.device.brand === 'Apple' && device.os.name === 'Mac') {
ctx.deviceType = 'ipad';
} else if (device.os.name === 'Windows') {
ctx.deviceType = 'pc';
} else {
ctx.deviceType = 'other';
}
console.log('clients: ',wss.clients instanceof Set);
const clientsArr = Array.from(wss.clients);
const numberIpads = clientsArr.filter(
(c) => c.deviceType === 'ipad'
).length;
wss.clients.forEach(function each(client) {
client.send(
JSON.stringify({
eventType: 'clientConn',numberClients: wss.clients.size,device,numberIpads
})
);
});
// handle message events
// receive a message and echo it back
ctx.on('message',(message) => {
console.log('raw message: |' + message + '|');
try {
const jsonMessage = JSON.parse(JSON.parse(message.trim()));
console.log(`Received message => ${jsonMessage}`);
console.log('Received JSON: ',jsonMessage);
//ctx.send(`you said ${message}`);
if (jsonMessage.eventType === 'movedOrder') {
const movedOrder = jsonMessage.movedOrder;
console.log('movedOrder: ',movedOrder);
wss.clients.forEach(function each(client) {
let fromMe = false;
if (client === ctx) {
fromMe = true;
}
const sendToAllMsg =
'Order ' +
movedOrder.orderNumber +
' was moved to line ' +
movedOrder.lineNumTo +
' pos ' +
movedOrder.linePosTo +
'!!';
// for now we are sending this message to all other
// clients besides the originator because it is causing
// a refresh when sending to the originating client
if (!fromMe) {
client.send(
JSON.stringify({
eventType: 'movedOrder',movedOrder: {
orderID: movedOrder.orderNumber,lineNumTo: movedOrder.lineNumTo,linePosTo: movedOrder.linePosTo,lineNumFrom: movedOrder.lineNumFrom,linePosFrom: movedOrder.linePosFrom
},msg: sendToAllMsg,fromMe
})
);
} else {
client.send('moved order!');
}
});
} else if (jsonMessage.eventType === 'clientLoggedIn') {
const pw = jsonMessage.pw;
const login = loginAccounts.find((la) => la.pw === pw);
if (login) {
if (!activeLogins.includes(ctx?.login?.pw)) {
// save that login with the client
ctx.login = login;
activeLogins.push(login.pw);
}
if (
activeLogins.length > 0 &&
activeLogins.some((l) => l !== login.pw)
) {
// another user is logged in,send warning message
const numLogins = activeLogins.filter(
(l) => l !== login.pw
).length;
let msg = 'There is already 1 user';
if (numLogins > 1) {
msg = `There are already ${numLogins} users`;
}
msg += ' (';
activeLogins
.filter((l) => l !== login.pw)
.forEach((l) => {
const theLogin = loginAccounts.find(
(la) => la.pw === l
);
msg += theLogin.first + ' ' + theLogin.last[0];
});
msg +=
') logged in to the Scheduling. Be careful about moving orders around.';
console.log('sending msg to the client: ' + msg);
ctx.send(
JSON.stringify({
eventType: 'multipleLogins',msg
})
);
}
} else {
}
}
} catch (e) {
console.log(e);
}
});
// handle close event
ctx.on('close',() => {
if (ctx.login) {
activeLogins = activeLogins.filter(function(e) {
return e !== ctx.login.pw;
});
}
console.log('removed - activeLogins: ',activeLogins);
console.log('closed',wss.clients.size);
const clientsArr = Array.from(wss.clients);
const numberIpads = clientsArr.filter(
(c) => c.deviceType === 'ipad'
).length;
wss.clients.forEach(function each(client) {
client.send(
JSON.stringify({
eventType: 'clientConn',numberIpads
})
);
});
//clearInterval(interval);
});
//sent a message that we're good to proceed
//ctx.send('connection established.');
});
};
我的React中确实有一些代码正在“接收” Node消息并适当地处理它们,但是我注释掉了该代码,但仍然遇到刷新问题。在这方面的任何帮助将不胜感激!
更新:在丹尼尔(Daniele)的帮助下,我发现实际上是客户端代码接收到导致刷新的消息。在注释掉该代码后,没有刷新发生。
这是我顶层组件中的代码直接从app.js中加载:
const ws = useGlobalWebSocketContext();
const numberClients = parseInt(
useGlobalWSDataContext().numberClients,10
);
const numberIpads = parseInt(
useGlobalWSDataContext().numberIpads,10
);
console.log('SchedulePage numberIpads: ' + numberIpads);
实际上,我删除了除了第一行以外的所有内容,刷新仍然发生了!
因此,大家可能都希望看到Web套接字上下文文件。
import React,{ createContext,useContext } from 'react';
import PropTypes from 'prop-types';
import useWebSocketLite from '../components/home/webSocketHook';
const GlobalWebSocketContext = createContext();
export const useGlobalWebSocketContext = () =>
useContext(GlobalWebSocketContext);
// websocket stuff
const wsURL =
process.env.NODE_ENV === 'development'
? 'ws://localhost'
: 'ws://production.app.local';
const GlobalWebSocketContextProvider = (props) => {
const websocket = useWebSocketLite({
socketUrl: wsURL + ':' + process.env.REACT_APP_WS_PORT
});
return (
<GlobalWebSocketContext.Provider value={websocket}>
{props.children}
</GlobalWebSocketContext.Provider>
);
};
并带有该webSocketHook.js:
import { useEffect,useState } from 'react';
// small utilities that we need
// handle json messages
const formatMessage = (data) => {
try {
return JSON.parse(data);
} catch (err) {
return data;
}
};
// get epoch timestamp
const getTimestamp = () => {
return new Date().getTime();
};
// define a custom hook
// accept the url to connect to
// number of times the hook should retry a connection
// the interval between retries
const useWebSocketLite = ({
socketUrl,retry: defaultRetry = 3,retryInterval = 1500
}) => {
// message and timestamp
const [data,setData] = useState();
// send function
const [send,setSend] = useState(() => () => undefined);
// state of our connection
const [retry,setRetry] = useState(defaultRetry);
// retry counter
const [readyState,setReadyState] = useState(false);
useEffect(() => {
// console.log('socketUrl: ' + socketUrl);
const ws = new WebSocket(socketUrl);
ws.onopen = () => {
// console.log('Connected to socket');
setReadyState(true);
// function to send messages
setSend(() => {
return (data) => {
try {
const d = JSON.stringify(data);
ws.send(d);
return true;
} catch (err) {
return false;
}
};
});
// receive messages
ws.onmessage = (event) => {
//const msg = formatMessage(event.data);
// setData({ message: msg,timestamp: getTimestamp() });
setData({ message: event.data });
// console.log(event.data);
};
};
// on close we should update connection state
// and retry connection
ws.onclose = () => {
setReadyState(false);
// retry logic
if (retry > 0) {
setTimeout(() => {
setRetry((retry) => retry - 1);
},retryInterval);
}
};
// terminate connection on unmount
return () => {
ws.close();
};
// retry dependency here triggers the connection attempt
},[retry]);
return { send,data,readyState };
};
export default useWebSocketLite;
解决方法
每次收到新数据时,都会调用ws.onmessage
,这反过来又调用setData
,该状态会更改状态,如果顶层组件中使用了useGlobalWebSocketContext
,则通常该组件将重新渲染
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。