我是一名 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,我也会得到相同的行为
可能原因:
useEffect() 中的匿名函数保留在内存中,并且 从未发布过。
页面上的某些内容保留在内存中,触发 useEffect() 反复。
我该如何解决这个问题?
PS:显然,我无法对问题进行 Stackblitz 重构...我的代码中的所有其他 useEffect() 都按其应有的方式运行。只有这个由于某种原因不想发布该功能。
如果您处于开发模式,reactjs 会运行 useEffect 之类的钩子两次,以便更轻松地捕获潜在错误。您可以通过在App.js中注释React.StrictMode来检查是否是这个原因