React Select下拉菜单因国旗加载卡顿,useTransition使用问题求助
解决React Select加载大量国旗卡顿问题
原代码问题分析
你把setMenuIsOpen(true)放在了startTransition回调里,这会把菜单打开的操作标记为低优先级过渡任务,导致React先完成其他渲染工作后才会显示菜单,和你想要的"立即打开菜单"需求完全相反。
修正方案:分离菜单打开与国旗加载逻辑
核心思路是:菜单打开操作立即执行,将国旗的渲染逻辑放入过渡任务中延迟执行,避免阻塞UI。
修正后的代码
import axios from "axios"; import React from "react"; import { useTransition, useEffect, useState } from "react"; import Select from "react-select"; import Flag from "react-world-flags"; const ReactSelectFlag = () => { const [countries, setCountries] = useState([]); const [isPending, startTransition] = useTransition(); const [menuIsOpen, setMenuIsOpen] = useState(false); const [showFlags, setShowFlags] = useState(false); useEffect(() => { axios.get("v1/countries").then(res => { setCountries(res.data.data); }); }, []); const handleMenuOpen = () => { // 立即打开菜单,不放入过渡任务 setMenuIsOpen(true); // 在过渡中触发国旗渲染,降低该操作优先级 startTransition(() => { setShowFlags(true); }); }; const handleMenuClose = () => { setMenuIsOpen(false); setShowFlags(false); }; return ( <Select options={countries} getOptionValue={option => option?.id} menuIsOpen={menuIsOpen} onMenuOpen={handleMenuOpen} onMenuClose={handleMenuClose} getOptionLabel={option => ( <div style={{ display: "flex", alignItems: "center" }}> {/* 仅当showFlags为true时渲染国旗 */} {showFlags && ( <span style={{ marginRight: "6px" }}> <Flag code={option?.code} width="24px" height="14px" /> </span> )} {option?.code} - {option?.name} </div> )} // 可选:添加过渡期间的加载提示 components={{ Option: props => { if (isPending && !showFlags) { return <div style={{ padding: "0.5rem" }}>加载国旗中...</div>; } return <Select.Option {...props} />; } }} /> ); }; export default ReactSelectFlag;
关键改动说明
- 新增
showFlags状态,控制国旗的渲染时机 handleMenuOpen中先直接调用setMenuIsOpen(true),确保菜单立即显示- 将
setShowFlags(true)放入startTransition,让React在空闲时渲染国旗,避免阻塞菜单打开的UI操作 - 可选:通过自定义
Option组件,在过渡期间显示加载提示,提升用户体验
进阶优化:虚拟滚动
如果国旗加载仍然存在性能问题,推荐使用虚拟滚动插件,只渲染可视区域内的选项,大幅减少DOM节点数量:
// 先安装依赖 // npm install react-select-virtualized import VirtualizedSelect from 'react-select-virtualized'; const ReactSelectFlag = () => { // ... 其他状态和逻辑不变 return ( <VirtualizedSelect options={countries} getOptionValue={option => option?.id} menuIsOpen={menuIsOpen} onMenuOpen={handleMenuOpen} onMenuClose={handleMenuClose} getOptionLabel={option => ( <div style={{ display: "flex", alignItems: "center" }}> <span style={{ marginRight: "6px" }}> <Flag code={option?.code} width="24px" height="14px" /> </span> {option?.code} - {option?.name} </div> )} // 设置菜单高度,限制可视区域 menuHeight={400} /> ); };
内容的提问来源于stack exchange,提问作者Rahil Əliyev
相关产品推荐
相关产品推荐

