我正在制作一个react应用程序与节点作为后端。它们使用socket-io进行通信。问题是,我有一个问题,反应和emit
发送数据到节点,但做了两次,从而使双重提交。例如,对于登录表单,终端输出如下所示:
[nodemon] starting `node --trace-warnings central-server.js`
web server running on port 5000
client GuRD1JAb_J1xn4O4AAAB successfully connected
{ CompanyID: 'BigCompany', Password: 'SecretPassword' }
{ CompanyID: 'BigCompany', Password: 'SecretPassword' }
这是前端应用程序:
import { useState } from "react";
import Form from "./components/Signup-Form.js";
import { io } from "socket.io-client";
const socket = io("http://192.168.43.118:5000", {
withCredentials: true,
extraHeaders: {
"react-client": "react-client",
},
});
function App() {
const [userData, setUserData] = useState({});
if (Object.entries(userData).length === 0) {
return (
<div className="App">
<div className="textContainer">
<h1>Welcome</h1>
<h3>You can Log in or Sign Up here</h3>
</div>
<Form setUserData={setUserData} />
</div>
);
} else {
socket.emit("signup", userData);
return <p>Data sent successfully</p>;
}
}
export default App;
服务器:
var express = require("express");
var app = express();
var server = require("http").Server(app);
const dotenv = require("dotenv");
dotenv.config();
const WS_PORT = process.env.PORT || 5000;
const io = require("socket.io")(server, {
cors: {
origin: ["http://localhost:3000", "http://192.168.43.118:3000"],
methods: ["GET", "POST"],
transports: ["websocket", "polling", "flashsocket"],
allowedHeaders: ["react-client"],
credentials: true,
},
});
io.on("connection", (socket) => {
console.log(`client ${socket.id} successfully connected`);
socket.on("signup", (data) => {
console.log(data);
});
});
server.listen(WS_PORT, () => console.log("web server running on port", WS_PORT));
App.js
文件是目前唯一使用套接字的文件。此外,当我保存文件和服务器自动重新加载时,我得到客户端创建连接3次,而不是仅仅一次。
您不应该在呈现代码中执行异步或事件驱动的操作。每次你的App
组件被渲染(例如当状态改变时),它也会再次执行你的socket.emit
,如果userData
仍然是空的。
作为什么导致App
渲染两次,我不能从你的代码告诉。但是你应该假设React可以随时重新渲染你的组件,所以尽量避免这样的问题。
您可以使用React.useEffect
和[]
的依赖数组,以便在组件挂载时仅执行一次操作。在您的情况下,最好只是有一个常规的回调:
import { useEffect, useState } from "react";
function App() {
const [dataSent, setDataSent] = useState(false);
const sendData = (data) => {
if (dataSent) return; // Just to make sure
socket.emit('signup', data);
setDataSent(true);
};
// If you want to actuall wait for a response to display:
const [response, setResponse] = useState(undefined);
useEffect(() => {
socket.on('signup-response', setResponse);
return () => {
// This function gets called when the effect gets retriggered
// (which never happens because of the [] dependency array)
// or if the component gets unmouted.
// We remove the event listener here:
socket.off('signup-response', setResponse);
};
}, []);
if (response) return <p>Got response: {response}</p>;
if (dataSent) {
return <p>Data sent successfully</p>;
} else {
return (
<div className="App">
<div className="textContainer">
<h1>Welcome</h1>
<h3>You can Log in or Sign Up here</h3>
</div>
<Form setUserData={sendData} />
</div>
);
}
}
我添加了一种方式,让您侦听来自服务器的响应。您可以将此用于任何数据/事件,服务器也可以多次发送这些数据/事件。