Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcda5657

Browse files
fixed assets loading on scroll
1 parentc11dcb5 commitcda5657

File tree

1 file changed

+116
-106
lines changed

1 file changed

+116
-106
lines changed

‎client/packages/lowcoder/src/comps/controls/iconscoutControl.tsx

Lines changed: 116 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
useIcon,
1313
wrapperToControlItem,
1414
}from"lowcoder-design";
15-
import{memo,ReactNode,useCallback,useMemo,useRef,useState}from"react";
15+
import{ReactNode,useCallback,useEffect,useMemo,useRef,useState}from"react";
1616
importstyledfrom"styled-components";
1717
importPopoverfrom"antd/es/popover";
1818
import{CloseIcon,SearchIcon}from"icons";
@@ -225,62 +225,85 @@ export const IconPicker = (props: {
225225
IconType?:"OnlyAntd"|"All"|"default"|undefined;
226226
})=>{
227227
constdraggableRef=useRef<HTMLDivElement>(null);
228-
const[visible,setVisible]=useState(false)
229-
const[loading,setLoading]=useState(false)
230-
const[downloading,setDownloading]=useState(false)
231-
const[searchText,setSearchText]=useState<string>('')
232-
const[searchResults,setSearchResults]=useState<Array<any>>([]);
233-
const{ subscriptions}=useSimpleSubscriptionContext();
234-
228+
const[visible,setVisible]=useState(false);
229+
const[loading,setLoading]=useState(false);
230+
const[downloading,setDownloading]=useState(false);
231+
const[searchText,setSearchText]=useState<string>('');
232+
const[searchResults,setSearchResults]=useState<Array<any>>([]);
235233
const[page,setPage]=useState(1);
236234
const[hasMore,setHasMore]=useState(true);
235+
constabortControllerRef=useRef<AbortController|null>(null);
236+
const{ subscriptions}=useSimpleSubscriptionContext();
237237

238-
239-
constmediaPackSubscription=subscriptions.find(
240-
sub=>sub.product===SubscriptionProductsEnum.MEDIAPACKAGE&&sub.status==='active'
238+
constmediaPackSubscription=useMemo(()=>
239+
subscriptions.find(
240+
sub=>sub.product===SubscriptionProductsEnum.MEDIAPACKAGE&&sub.status==='active'
241+
),
242+
[subscriptions]
241243
);
242244

243245
constonChangeRef=useRef(props.onChange);
244246
onChangeRef.current=props.onChange;
245247

248+
// Cleanup function for async operations
249+
useEffect(()=>{
250+
return()=>{
251+
if(abortControllerRef.current){
252+
abortControllerRef.current.abort();
253+
}
254+
};
255+
},[]);
256+
246257
constonChangeIcon=useCallback(
247258
(key:string,value:string,url:string)=>{
248259
onChangeRef.current(key,value,url);
249260
setVisible(false);
250-
},[]
261+
},
262+
[]
251263
);
252264

253-
constfetchResults=async(query:string,pageNum:number=1)=>{
265+
constfetchResults=useCallback(async(query:string,pageNum:number=1)=>{
266+
if(abortControllerRef.current){
267+
abortControllerRef.current.abort();
268+
}
269+
abortControllerRef.current=newAbortController();
270+
254271
setLoading(true);
272+
try{
273+
const[freeResult,premiumResult]=awaitPromise.all([
274+
searchAssets({
275+
...IconScoutSearchParams,
276+
asset:props.assetType,
277+
price:'free',
278+
query,
279+
page:pageNum,
280+
}),
281+
searchAssets({
282+
...IconScoutSearchParams,
283+
asset:props.assetType,
284+
price:'premium',
285+
query,
286+
page:pageNum,
287+
})
288+
]);
255289

256-
constfreeResult=awaitsearchAssets({
257-
...IconScoutSearchParams,
258-
asset:props.assetType,
259-
price:'free',
260-
query,
261-
page:pageNum,
262-
});
263-
264-
constpremiumResult=awaitsearchAssets({
265-
...IconScoutSearchParams,
266-
asset:props.assetType,
267-
price:'premium',
268-
query,
269-
page:pageNum,
270-
});
271-
272-
constcombined=[...freeResult.data, ...premiumResult.data];
273-
constisLastPage=combined.length<IconScoutSearchParams.per_page*2;
274-
275-
setSearchResults(prev=>
276-
pageNum===1 ?combined :[...prev, ...combined]
277-
);
278-
setHasMore(!isLastPage);
279-
setLoading(false);
280-
};
281-
290+
constcombined=[...freeResult.data, ...premiumResult.data];
291+
constisLastPage=combined.length<IconScoutSearchParams.per_page*2;
292+
293+
setSearchResults(prev=>
294+
pageNum===1 ?combined :[...prev, ...combined]
295+
);
296+
setHasMore(!isLastPage);
297+
}catch(error:any){
298+
if(error.name!=='AbortError'){
299+
console.error('Error fetching results:',error);
300+
}
301+
}finally{
302+
setLoading(false);
303+
}
304+
},[props.assetType]);
282305

283-
constdownloadAsset=async(
306+
constdownloadAsset=useCallback(async(
284307
uuid:string,
285308
downloadUrl:string,
286309
callback:(assetUrl:string)=>void,
@@ -293,29 +316,29 @@ export const IconPicker = (props: {
293316
});
294317
}
295318
}catch(error){
296-
console.error(error);
319+
console.error('Error downloading asset:',error);
297320
setDownloading(false);
298321
}
299-
}
322+
},[]);
300323

301-
constfetchDownloadUrl=async(uuid:string,preview:string)=>{
324+
constfetchDownloadUrl=useCallback(async(uuid:string,preview:string)=>{
302325
try{
303326
setDownloading(true);
304327
constresult=awaitgetAssetLinks(uuid,{
305328
format:props.assetType===AssetType.LOTTIE ?'lottie' :'svg',
306329
});
307330

308-
downloadAsset(uuid,result.download_url,(assetUrl:string)=>{
331+
awaitdownloadAsset(uuid,result.download_url,(assetUrl:string)=>{
309332
setDownloading(false);
310333
onChangeIcon(uuid,assetUrl,preview);
311334
});
312335
}catch(error){
313-
console.error(error);
336+
console.error('Error fetching download URL:',error);
314337
setDownloading(false);
315338
}
316-
}
339+
},[props.assetType,downloadAsset,onChangeIcon]);
317340

318-
consthandleChange=(e:{target:{value:any;};})=>{
341+
consthandleChange=useCallback((e:{target:{value:any;};})=>{
319342
constquery=e.target.value;
320343
setSearchText(query);// Update search text immediately
321344

@@ -324,9 +347,15 @@ export const IconPicker = (props: {
324347
}else{
325348
setSearchResults([]);// Clear results if input is too short
326349
}
327-
};
328-
329-
constdebouncedFetchResults=useMemo(()=>debounce(fetchResults,700),[]);
350+
},[]);
351+
352+
constdebouncedFetchResults=useMemo(
353+
()=>debounce((query:string)=>{
354+
setPage(1);
355+
fetchResults(query,1);
356+
},700),
357+
[fetchResults]
358+
);
330359

331360
constrowRenderer=useCallback(
332361
({ index, key, style}:ListRowProps)=>{
@@ -408,39 +437,41 @@ export const IconPicker = (props: {
408437
</IconRow>
409438
);
410439
},
411-
[columnNum,mediaPackSubscription,props.assetType,fetchDownloadUrl]
440+
[columnNum,mediaPackSubscription,props.assetType,fetchDownloadUrl,searchResults]
412441
);
413-
414442

415443
constpopupTitle=useMemo(()=>{
416444
if(props.assetType===AssetType.ILLUSTRATION)returntrans("iconScout.searchImage");
417445
if(props.assetType===AssetType.LOTTIE)returntrans("iconScout.searchAnimation");
418446
returntrans("iconScout.searchIcon");
419447
},[props.assetType]);
420448

421-
constMemoizedIconList=memo(({
422-
searchResults,
423-
rowRenderer,
424-
onScroll,
425-
columnNum,
449+
consthandleScroll=useCallback(({
450+
clientHeight,
451+
scrollHeight,
452+
scrollTop,
426453
}:{
427-
searchResults:any[];
428-
rowRenderer:(props:ListRowProps)=>React.ReactNode;
429-
onScroll:(params:{clientHeight:number;scrollHeight:number;scrollTop:number})=>void;
430-
columnNum:number;
454+
clientHeight:number;
455+
scrollHeight:number;
456+
scrollTop:number;
431457
})=>{
432-
return(
433-
<IconList
434-
width={550}
435-
height={400}
436-
rowHeight={140}
437-
rowCount={Math.ceil(searchResults.length/columnNum)}
438-
rowRenderer={rowRenderer}
439-
onScroll={onScroll}
440-
/>
441-
);
442-
});
443-
458+
if(hasMore&&!loading&&scrollHeight-scrollTop<=clientHeight+10){
459+
constnextPage=page+1;
460+
setPage(nextPage);
461+
fetchResults(searchText,nextPage);
462+
}
463+
},[hasMore,loading,page,searchText,fetchResults]);
464+
465+
constmemoizedIconListElement=useMemo(()=>(
466+
<IconList
467+
width={550}
468+
height={400}
469+
rowHeight={140}
470+
rowCount={Math.ceil(searchResults.length/columnNum)}
471+
rowRenderer={rowRenderer}
472+
onScroll={handleScroll}
473+
/>
474+
),[searchResults.length,rowRenderer,handleScroll,columnNum]);
444475

445476
return(
446477
<Popover
@@ -471,11 +502,6 @@ export const IconPicker = (props: {
471502
/>
472503
<StyledSearchIcon/>
473504
</SearchDiv>
474-
{loading&&(
475-
<Flexalign="center"justify="center"style={{flex:1}}>
476-
<Spinindicator={<LoadingOutlinedstyle={{fontSize:25}}spin/>}/>
477-
</Flex>
478-
)}
479505
<Spinspinning={downloading}indicator={<LoadingOutlinedstyle={{fontSize:25}}/>}>
480506
{!loading&&Boolean(searchText)&&!Boolean(searchResults?.length)&&(
481507
<Flexalign="center"justify="center"style={{flex:1}}>
@@ -484,33 +510,16 @@ export const IconPicker = (props: {
484510
</Typography.Text>
485511
</Flex>
486512
)}
487-
{!loading&&Boolean(searchText)&&Boolean(searchResults?.length)&&(
513+
{Boolean(searchText)&&Boolean(searchResults?.length)&&(
488514
<IconListWrapper>
489-
490-
<IconList
491-
width={550}
492-
height={400}
493-
rowHeight={140}
494-
rowCount={Math.ceil(searchResults.length/columnNum)}
495-
rowRenderer={rowRenderer}
496-
onScroll={({
497-
clientHeight,
498-
scrollHeight,
499-
scrollTop,
500-
}:{
501-
clientHeight:number;
502-
scrollHeight:number;
503-
scrollTop:number;
504-
})=>{
505-
if(hasMore&&!loading&&scrollHeight-scrollTop<=clientHeight+10){
506-
constnextPage=page+1;
507-
setPage(nextPage);
508-
fetchResults(searchText,nextPage);
509-
}
510-
}}
511-
/>
515+
{memoizedIconListElement}
512516
</IconListWrapper>
513517
)}
518+
{loading&&(
519+
<Flexalign="center"justify="center"style={{flex:1}}>
520+
<Spinindicator={<LoadingOutlinedstyle={{fontSize:25}}spin/>}/>
521+
</Flex>
522+
)}
514523
</Spin>
515524
</PopupContainer>
516525
</Draggable>
@@ -557,11 +566,12 @@ export function IconscoutControl(
557566
){
558567
returnclassIconscoutControlextendsSimpleComp<IconScoutAsset>{
559568
readonlyIGNORABLE_DEFAULT_VALUE=false;
569+
560570
protectedgetDefaultValue():IconScoutAsset{
561571
return{
562-
uuid:'',
563-
value:'',
564-
preview:'',
572+
uuid:"",
573+
value:"",
574+
preview:"",
565575
};
566576
}
567577

@@ -586,5 +596,5 @@ export function IconscoutControl(
586596
</ControlPropertyViewWrapper>
587597
);
588598
}
589-
}
599+
};
590600
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp