使用 socket.io 将新的数组更改应用到 useEffect 并更改流

Posted

技术标签:

【中文标题】使用 socket.io 将新的数组更改应用到 useEffect 并更改流【英文标题】:Apply new array changes to useEffect with socket.io and change streams 【发布时间】:2021-12-19 02:43:58 【问题描述】:

我目前正在构建一个社交平台,我使用 Mongo Change Streams 来检测任何更改,这些更改将“发出”到单独的套接字,我之前遇到的问题是,一切正常非常好,但现在由于嵌套 cmets 和回复的实现,我的数据库结构发生了变化。因此,我想出了一个解决方法,将三个单独的数组合并为一个数组,其中包含与喜欢和 cmets 关联的嵌套数组信息。所以我想要的是我想从单独的套接字(posts、likes、cmets)中获取数据并合并为一个,因此我已经应用了一个处理程序来检查两者之间的任何变化新旧数据,所以基本上我只是坚持将这三个套接字数据合并到一个数组中,我有点迷失了如何采用这种方法。

更改流

const db = mongoose.connection;
db.once('open', () => 
    //post changes
    console.log(chalk.blueBright("Setting change streams"));
    db.collection('posts').watch()
    .on('change', change => 
        if(change.operationType === 'insert')
            console.log(chalk.yellowBright('INSERTED'))
            io.emit('posts', change.fullDocument);
            console.log(change.fullDocument)
        
    );
    db.collection('posts').watch( fullDocument: 'updateLookup' )
    .on('change', change => 
        if(change.operationType === 'update')
            console.log(chalk.yellowBright('UPDATED'));
            io.emit('posts', change.fullDocument);
            console.log(change.fullDocument)
        
    )

    //comments changes
    db.collection('comments').watch( fullDocument: 'updateLookup' )
    .on('change', change => 
        if(change.operationType === 'insert')
            console.log(chalk.yellowBright('INSERTED'))
            io.emit('comments', change.fullDocument);
            console.log(change.fullDocument)
        
    );
    db.collection('comments').watch( fullDocument: 'updateLookup' )
    .on('change', change => 
        if(change.operationType === 'update')
            console.log(chalk.yellowBright('UPDATED'));
            io.emit('comments', change.fullDocument);
            console.log(change.fullDocument)
        
    )
); 

客户端

useEffect 用于检测任何更改帖子

//Check socket changes
    useEffect(() => 
      //merged posts with comments and likes sockets?
      //this is the solution I've come up on merging three arrays but
      //I'd like to apply this with the three sockets data that are being retrieved or applied
      const newPosts = posts.map(post => (
        ...post,
        likes: likes.filter(like => post.likes.map(like => like._id).includes(like._id)),
        comments: comments.filter(comment => post.comments.map(comment => comment._id).includes(comment._id)),
      ))

      const handler = (item) => 
        setPosts((oldPosts) => 
          const findItem = oldPosts.find((post) => post._id === item._id);
          if (findItem) 
            return oldPosts.map((post) => (post._id === item._id ? item : post));
           else 
            return [item, ...oldPosts];
          
        );   
      ;

      //Sockets
      //socket.off("comments", handler);
      //socket.off("likes", handler);

      return () => socket.off("posts", handler);
       //eslint-disable-next-line react-hooks/exhaustive-deps
    , []);

【问题讨论】:

这个useEffect 在安装时只运行一次。它永远不会运行,因为您没有传递任何依赖项。如果您希望它在posts 更改时运行,您必须在最后定义的空数组中添加posts 作为依赖项。将其移动到另一个 useEffect 调用中,否则 socket.off 也会调用 posts 中的每个更改 这不是原因,因为它在将更改应用于状态时工作得非常好,就像我提到的一切工作正常,但由于我正在使用三个不同的套接字,我想找到一种将套接字输出数据合并到一个数组的方法,因此传递给处理程序的数组将是来自套接字的新合并数组。如果这有意义 【参考方案1】:

对此有两种解决方案,第一个是将调用套接字的方式更改为socket.onMany(handler),它添加了一个侦听器,当任何事件发出时都会触发该侦听器。在那里,输出将存储为[["posts",],["comment",],["likes"],],接下来要做的是使用“.splice(indexOf('posts'))”删除字符串值,然后你需要将三个数组展平为对象,并通过关联的 id 的喜好将它们相互合并:

const newPosts = posts.map(post => (
    ...post,
    likes: likes.filter(like => post.likes.map(like => like.id).includes(like.id)),
    comments: comments.filter(comment => post.comments.map(comment => comment.id).includes(comment.id)),
))

第二种方法是修改更改流,调用 Post 模型并使用 populate(),它每次都会根据需要使用填充的数据向数据库调用一个新请求,然后将其发送到您的套接字,因此由于帖子将是父文档,因此只应在帖子套接字中检查更改。

//post changes
    console.log(chalk.blueBright("Setting change streams"));
    db.collection('posts').watch()
    .on('change', change => 
        if(change.operationType === 'insert')
            PostModel.find().lean().populate('comments')
            .then((posts) => io.emit('posts', posts))
        
    )
    db.collection('posts').watch( fullDocument: 'updateLookup' )
    .on('change', change => 
        if(change.operationType === 'update')
            PostModel.find().lean().populate('comments')
            .then((posts) => io.emit('posts', posts))
        
    )

【讨论】:

以上是关于使用 socket.io 将新的数组更改应用到 useEffect 并更改流的主要内容,如果未能解决你的问题,请参考以下文章

C++ 将新的类对象添加到数组中的新位置

将新的键/值对添加到 PostgreSQL JSON 列内的嵌套数组中

在控制器中使用 socket.io

MongoDB 更改流与 socket.io

将新的/更改的文件从一个文件夹复制到另一个文件夹

将新的子文档附加到主结构中的数组