首页 > 解决方案 > BehaviorSubject 订阅导致 React 组件中的无限循环

问题描述

我正在尝试使用绑定到 SocketIO 实现的 BehaviorSubject 来向订阅它的所有组件广播更改。我使用它是因为我希望能够将“旧”值发送给新订阅者。

这是我的客户端套接字实现:

// socket.js

const { Observable, Subject, BehaviorSubject } = require('rxjs');
const io = require('socket.io-client');
const axios = require('axios');

const { environment } = require('../../environment/environment');

module.exports = class {
  this.socket = io(environment.api.notifications);
  this.notifications$ = new BehaviorSubject();

  this.listener$ = new Observable(observer => {
    // listening for NEW_NOTIFICATIONS event from socket server
    this.socket.on('NEW_NOTIFICATIONS', () => observer.next());
  }).subscribe(async () => {
    // hit the API for notifications when the event occurs 
    console.log('listener sub called')
    const notifs = await this.getNotifications();

    // tell the BehaviorSubject to dispatch the new notifications
    this.notifications$.next(notifs);
  });

  async getNotifications() {
    console.log('getNotifications')
    try {
      let response = await axios({
        method: 'get',
        url: `${environment.api.notifications}/notifications/getAll`,
      });

      return JSON.parse(response.data.body);
    } catch (err) {
      if (err) {
        console.error('error getting notifs:', err);
        observer.error(err);
      }
    }
  }
};

is 在应用程序的其他地方初始化...

// index.js

const Notifications = require('../socket.js');

module.exports = new Notifications();

这是使用该类的组件:

// component.tsx

import React, { ReactElement, useState } from 'react';
import { BehaviorSubject } from 'rxjs';

import Notifications from '../../index.js';

export const Component = (): ReactElement => {
  const [notifications, setNotifications]: [any[], Function] = useState([]);
  const notifs$: BehaviorSubject<any> = Notifications.notifications$;
  
  notifs$.subscribe(notifs => {
    console.log('notifs!', notifs);
    setNotifications(notifs);
  });

结果是:

listener sub called
socket.js: getNotifications
Component.tsx: notifications: Array(4)
Component.tsx: notifications: Array(4)
Component.tsx: notifications: Array(4)
Component.tsx: notifications: Array(4)
...
zone-evergreen.js:171 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
    at renderWithHooks (react-dom.development.js:14815)
    at updateFunctionComponent (react-dom.development.js:17034)
    at beginWork (react-dom.development.js:18610)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Zone.runTask (zone-evergreen.js:167)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480)
    at invokeTask (zone-evergreen.js:1621)
    at HTMLUnknownElement.globalZoneAwareCallback (zone-evergreen.js:1647)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)

我可以说这notifs$.subscribe是“导致”无限循环的原因,因为其余的可观察流函数仅按预期调用一次,但我不确定是什么告诉它运行。据我了解,订阅者Notifications.notifications$应该只在被调用时“触发” .next(),但它们似乎是递归触发的。套接字客户端在循环中只看到NEW_NOTIFICATIONS一次事件,因此它应该只触发一次。

标签: javascriptreactjssocket.iorxjs

解决方案


推荐阅读