如何在控制器内部使用 socketio?

How to use socketio inside controllers?

我有一个使用 Reactjs、Redux、Nodejs 创建的应用程序,MongoDB。我在后端创建了 socketio。

server.js


const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();

const routes = require('./routes/api/routes');
var server = require('http').Server(app);
const io = require('./socket').init(server);
app.use(express.json());

require('dotenv').config();
mongoose.connect(process.env.MONGO_DB).then(console.log('connected'));
const corsOptions = {
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus: 200,
};

app.use(cors(corsOptions));

app.use(routes);

if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging'
) {
// Set static folder
app.use(express.static('client/build'));

app.get('*', (req, res) => {
 res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}

io.on('connection', (socket) => {
console.log('New client connected');

socket.on('disconnect', () => {
 console.log('Client disconnected');
});
});

server.listen(process.env.PORT || 5000, () => {
console.log('socket.io server started on port 5000');
});

socket.js

let io;

module.exports = {
  init: (httpServer) => {
    return (io = require('socket.io')(httpServer, {
      cors: {
        origin: 'http://localhost:3000',
        methods: ['GET', 'POST'],
      },
    }));
  },

  getIO: () => {
    if (!io) {
      throw new Error('Socket.io is not initialized');
    }
    return io;
  },
};

controller.js 中,我正在 mongodb 中创建新项目,并且还使用了 socketio。我正在发出我创建的新项目。看起来是这样

controller.js


const createTeam = async (req, res) => {
  const userId = req.user.id;
  const newItem = new Item({
    name: req.body.name,
    owner: userId,
  });
  await newItem.save((err, data) => {
    if (err) {
      console.log(err);
      return res.status(500).json({
        message: 'Server Error',
      });
    }
  });
  await newItem.populate({
    path: 'owner',
    model: User,
    select: 'name',
  });

  await User.findByIdAndUpdate(
    userId,
    { $push: { teams: newItem } },
    { new: true }
  );

  io.getIO().emit('team', {
    action: 'creating',
    team: newItem,
  });

  res.json(newItem);
};

在前端,我正在使用 socket.io-client 监听 socketio 服务器。在我的 App.js 中,当我 console.log(data) 时,我可以看到来自后端的数据。在此阶段之前,我的应用程序运行完美。我可以从 socketio 获取数据,我可以看到连接的新客户端。但是当我使用 dispatch 发送数据时,我的应用程序开始向数据库添加无限的新项目。看看我的App.js

import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { AppNavbar } from './components/AppNavbar';
import { TeamList } from './components/TeamList';
import { loadUser } from './Store/Actions/AuthActions';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { io } from 'socket.io-client';
import { addItems } from './Store/Actions/itemActions';

function App() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(loadUser());
    const socket = io('http://localhost:5000');
    socket.on('team', (data) => {
      if (data.action === 'creating') {
        dispatch(addItems(data.team));
      }
      // console.log(data);
    });
  }, []);

  return (
    <div className="App">
      <AppNavbar />
      <TeamList />
    </div>
  );
}

export default App;

我在redux端的itemAction也是这样

export const addItems = (input) => (dispatch, getState) => {
  axios
    .post('/api/items/createTeam', input, tokenConfig(getState))
    .then((res) =>
      dispatch({
        type: ADD_ITEM,
        payload: res.data,
      })
    )
    .catch((err) =>
      dispatch(
        returnErrors(err.response.data, err.response.status, 'GET_ERRORS')
      )
    );
};

我的问题是如何在实现 socketio 后停止对 api 的无限调用。如何有效地停止死循环?

无限循环源于未调度“类型:ADD_ITEM”once.This 问题导致您的 itemAction 总是会在每次之后调度“类型:ADD_ITEM”和有效负载然后你的反应会 re-render 一次又一次地翻页。

您应该摆脱在 addItems 函数内的调度操作,并仅在 App.js 文件中的 useEffect 内调度您的操作。

您的代码片段在 App.js 中应如下所示:

useEffect(() => {
    //...
    const socket = openSocket('http://localhost:5000');
    socket.on('postsChannel', (data) => {
      if (data.action === 'creating') {
        dispatch({ type: ADD_ITEM, payload: data.team });
      }
    });
}, [dispatch]);