我的 UseEffect() 函数没有被释放(内存泄漏)

问题描述 投票:0回答:1

我是一名 Angular 开发人员,正在开发一个新的 React 应用程序(我正在学习)。有一个概念我觉得我不明白。

import './LeadPool.scss';
import SideMenuLayout from "../../layouts/side-menu/SideMenuLayout";
import {
  IonButton,
  IonCard,
  IonCardContent,
  IonChip,
  IonCol,
  IonRow,
  IonSearchbar,
  IonToggle, useIonLoading,
  useIonModal
} from "@ionic/react";
import React, {useEffect, useState} from "react";
import {Table, TableColumnsType, Tooltip} from "antd";
import {useParams} from "react-router-dom";
import {useService} from "../../store/service-provider/ServiceProvider";
import {LeadBo} from "../../services/lead-search/bos/leadBo";
import {PageBo} from "../../services/lead-search/bos/pageBo";
import {LeadPoolDataTypeInterface} from "./interfaces/lead-pool-data-type.interface";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import LeadViewer from "../../components/lead-viewer/LeadViewer";
import {OverlayEventDetail} from "@ionic/react/dist/types/components/react-component-lib/interfaces";
import Lottie from "lottie-react";
import pulsingAnimation from '../../assets/lotties/pulsing.json';

const LeadPool: React.FC = () => {

  const [datatable, setDatatable] = useState<LeadPoolDataTypeInterface[]>(null);
  const [pageLeadBoList, setPageLeadBoList] = useState<LeadBo[]>(null);
  const [selectedLeadBo, setSelectedLeadBo] = useState<LeadBo>(null);
  const services = useService();
  const { leadPoolId }  = useParams<{leadPoolId: string}>();
  const [presentLoading, dismissLoading] = useIonLoading();
  const [page, setPage] = useState<PageBo>({
    pageSize: 15,
    currentPage: 1,
    searchText: '',
    leadPoolId: leadPoolId,
    totalDocuments: 44,
    searchAllLeads: true,
  });
  const columns: TableColumnsType<LeadPoolDataTypeInterface> = [
    {
      title: 'Company',
      dataIndex: 'companyName',
      key: 'companyName',
      render: (value, record, index) =>
        <div>
          <a onClick={ (event)=> {
            startLeadViewer(record.id);
          }}>{ value }</a>
        </div>,
    },
    { title: 'Address', dataIndex: 'address', key: 'address' },
    {
      title: 'Social Media',
      dataIndex: 'socialMediaLinkList',
      key: 'socialMediaLinkList',
      render: (value, record, index) =>
        <div>
          { record.socialMediaLinkList.map( (socialMediaLink, index) => {
            return  (
              <Tooltip title={'Visit social'} key={record.id + '_' + index}>
                <a href={socialMediaLink.link}
                   target="_blank"
                   rel="noopener noreferrer">
                  <FontAwesomeIcon
                    icon={socialMediaLink.icon}
                    size={"2x"}
                    color={'gray'}
                    className="ml-2"
                    onClick={ (event) => {
                      console.log('onClick event', event);
                    }}
                  />
                </a>
              </Tooltip>
            )
          })}
        </div>,
    },
    {
      title: 'Phones',
      dataIndex: 'phone',
      key: 'phone',
      render: (value, record, index) => {
        if(record.phoneList.length > 0) {
        return (
            <div>
              <IonChip
                color="tertiary"
              >
                { record.phoneList[0].phoneNumber }
                <b style={{marginLeft: '0.5em'}}>{(record.phoneList.length > 1) ? '(+' + record.phoneList.length + ')' : ''}</b>
              </IonChip>
            </div>
        )}
      }
    },
    {
      title: 'Emails',
      dataIndex: 'email',
      key: 'email',
      render: (value, record, index) => {
        if(record.emailList.length > 0) {
          return (
            <div>
              <IonChip
                color="primary"
              >
                { record.emailList[0].email }
                <b style={{marginLeft: '0.5em'}}>{(record.emailList.length > 1) ? '(+' + record.emailList.length + ')' : ''}</b>
              </IonChip>
            </div>
          )}
      }
    },
    {
      title: 'Status',
      dataIndex: 'businessStatus',
      key: 'businessStatus',
      render: (value, record, index) => {
        if(record.isOperational) {
          return  <Lottie animationData={pulsingAnimation}
                          loop={true}
                          className="lead-pool-pulsing"
                  />
        }
      }
    },
  ];
  let isLeadViewerOpening = false;

  function generateDatatable(leadBoList: LeadBo[]) {
    const datatableList: LeadPoolDataTypeInterface[] = [];
    leadBoList.forEach( leadBo => {
      const datatable: LeadPoolDataTypeInterface = {
        key: leadBo.id,
        id: leadBo.id,
        companyName: leadBo.name,
        address: leadBo.fullAddress,
        socialMediaLinkList: leadBo.getSocialMediaLinkList(),
        phoneList: leadBo.getPhoneList(),
        emailList: leadBo.getEmailList(),
        isOperational: leadBo.isOperational(),
      }
      datatableList.push(datatable);
    })
    return datatableList;
  }


  function refreshPage(pageLocal: PageBo) {
    console.log('Kick11111 refreshPage', null);
      // setDatatable(null)
      // services.leadSearchService.getLeadPool(pageLocal).then((leadPoolBo) => {
      //   setPageLeadBoList(leadPoolBo.leadList);
      //   setDatatable( generateDatatable(leadPoolBo.leadList) );
      //   setPage( { ...pageLocal, totalDocuments: leadPoolBo.totalDocumentCount });
      // });
  }


  useEffect(() => {
    console.log('Kick222222 useEffect', null);
    refreshPage( page )
  }, []);


  const [presentLeadViewer, dismissLeadViewer] = useIonModal(LeadViewer, {
    onDismiss: (data: string, role: string) => {
      dismissLeadViewer(data, role)
    },
    leadBo: selectedLeadBo,
  });


  async function startLeadViewer(leadId: string){
    if(isLeadViewerOpening) { return }
    isLeadViewerOpening = true;
    presentLoading('Loading...');
    console.log('Kick11111 pageLeadBoList', pageLeadBoList);
    setSelectedLeadBo( pageLeadBoList.find( lead => lead.id == leadId) );
    presentLeadViewer({
      onWillDismiss: (ev: CustomEvent<OverlayEventDetail>) => {},
      cssClass: 'fullscreen-modal',
      onDidPresent: () => {
        dismissLoading();
        isLeadViewerOpening = false
      },
    } );
  }


  const rowSelection = {
    onChange: (selectedRowKeys: React.Key[], selectedRows: LeadPoolDataTypeInterface[]) => {
      console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
    }
  };


  return (
    <SideMenuLayout>
      <div className="container lead-pool">

        <section className="gr-p-t-14">
          <h1 className="mb-2">Lead Pool</h1>
          <p>Review all your leads and start marketing to them</p>
        </section>

        <section className="gr-m-t-5">
          <IonCard>
            <IonCardContent>
              <h2>Summary</h2>
            </IonCardContent>
          </IonCard>
        </section>

        <section className="gr-m-t-2">
          <IonCard className="ion-no-margin gr-p-2 gr-p-t-5 gr-p-b-5">
            <IonCardContent>

              <IonRow className="gr-p-b-2">
                <IonButton color="primary"
                           onClick={() => {
                           }}
                >
                  Start to market your leads
                </IonButton>
                <IonButton fill="clear" >
                  Import from CSV
                </IonButton>
                <IonButton fill="clear">
                  Export to CSV
                </IonButton>
              </IonRow>

              <div className="lead-pool__content__datatable">

                <div className="lead-pool__content__datatable__filter">
                  <IonRow>
                    <IonCol className="flex justify-start items-center">
                      <h4 className="gr-p-l-2">Leads</h4>
                    </IonCol>
                    <IonCol className="flex justify-start items-center">
                      <IonToggle checked={!page.searchAllLeads}
                                 onIonChange={(event) => {
                                   refreshPage({...page, searchAllLeads: !event.detail.checked})
                                 }}
                      >
                        Show leads from all searches
                      </IonToggle>
                    </IonCol>
                    <IonCol className="flex items-center">
                      <IonSearchbar debounce={400}
                                    onIonInput={(ev) => {
                                      const target = ev.target as HTMLIonSearchbarElement;
                                      refreshPage({...page, searchText: target.value});
                                    }}
                      />
                    </IonCol>
                  </IonRow>
                </div>

                <Table
                  className="lead-pool__content__datatable__table"
                  columns={columns}
                  pagination={{
                    current: page.currentPage,
                    pageSize: page.pageSize,
                    total: page.totalDocuments,
                    onChange: (pageNumber) => {
                      refreshPage({...page, currentPage: pageNumber});
                    }
                  }}
                  dataSource={datatable}
                  loading={(datatable == null)}
                />
              </div>
            </IonCardContent>
          </IonCard>
        </section>

        <Disclaimer />

      </div>
    </SideMenuLayout>
  );
};

export default LeadPool;

我希望 useEffect 只运行一次。我知道开发双跑;这不是问题。

站点地图:仪表板页面 > 潜在客户页面

从仪表板导航到首页时,useEffect() 按预期运行,触发刷新页面()。但是,如果我来回移动,引导页 API 调用就会累积(2、4、6 等)。仪表板页面有类似的useEffect(),但它不会堆积请求。

与服务无关。即使使用 console.log,我也会得到相同的行为

Screenshot of the network packages

可能原因:

  1. useEffect() 中的匿名函数保留在内存中,并且 从未发布过。

  2. 页面上的某些内容保留在内存中,触发 useEffect() 反复。

我该如何解决这个问题?

PS:显然,我无法对问题进行 Stackblitz 重构...我的代码中的所有其他 useEffect() 都按其应有的方式运行。只有这个由于某种原因不想发布该功能。

reactjs react-hooks
1个回答
0
投票

如果您处于开发模式,reactjs 会运行 useEffect 之类的钩子两次,以便更轻松地捕获潜在错误。您可以通过在App.js中注释React.StrictMode来检查是否是这个原因

© www.soinside.com 2019 - 2024. All rights reserved.