(已编辑)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';
    import {Disclaimer} from "@cleverleads/ui";
    
    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('Kick11111 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(),但它不会堆积请求。

enter image description here

可能原因:

1- useEffect() 中的匿名函数保留在内存中并且永远不会被释放。

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

我该如何解决这个问题?

reactjs react-hooks
1个回答
-1
投票

React 18 及以上版本使用“严格模式”。 “当严格模式开启时,React 将在第一次真正设置之前运行一个额外的仅开发设置+清理周期。这是一个压力测试,可确保您的清理逻辑“镜像”您的设置逻辑,并停止或撤消任何操作如果这导致问题,请实施清理功能。”来源

有关严格模式的更多信息: https://react.dev/reference/react/StrictMode

我不能确定是什么导致你的 useEffect 钩子继续增量运行,但有一些解决方案。

  1. 使用依赖项列表(末尾的数组)。该钩子将监视依赖项的更改,并且仅在检测到它们时才再次运行。 查看依赖关系
  2. 传递一个清理函数。 更多信息

希望这有帮助!

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