react-select如何通过API异步数据设置defaultValue默认值
问题根因
你的判断是对的,问题出在异步加载时序上:react-select的defaultValue属性只在组件首次挂载时读取一次,后续state更新不会触发它重新赋值。
你当前代码里组件第一次渲染的时候,defaultCity是初始空对象,cities是空数组,Select初始化拿不到有效默认值;等两个异步接口返回数据、state更新完成时,Select早就挂载完了,不会再重新读defaultValue,所以哪怕你打印defaultCity值是正确的,组件也不会显示选中状态。
修复方案
两种常用写法,优先选第一种,是React生态的标准受控用法,不会踩时序坑。
方案1:改成受控组件(推荐)
把非受控的defaultValue换成受控的value属性,绑定onChange事件同步选中值,只要state更新,Select就会自动同步选中状态,完全不受异步加载顺序影响:
首先调整状态和接口逻辑:
const [request, setRequest] = useState<IRequest>(); const [cities, setCities] = useState<ICity[]>([]); // 直接维护当前选中的城市值,不需要单独的defaultCity state const [selectedCity, setSelectedCity] = useState<{value: string, label: string} | null>(null); useEffect(() => { const initData = async () => { // 用Promise.all并行拉取两个接口,比分开写更快 const [requestRes, citiesRes] = await Promise.all([ axios.get<IRequest>(`/api/requests/${id}`), axios.get<ICity[]>('/api/cities') ]) setRequest(requestRes.data) setCities(citiesRes.data) // 接口返回后设置默认选中值 setSelectedCity({ value: requestRes.data.city.code, label: requestRes.data.city.name }) } initData() }, [])
然后修改Select组件的属性:
<Select placeholder='City' value={selectedCity} onChange={(newVal) => setSelectedCity(newVal as {value: string, label: string})} options={ cities.map(city => ({ value: city.code, label: city.name })) } />
方案2:数据加载完成后再渲染Select
如果你想保留非受控写法,就加一层加载判断,等城市列表、默认城市值都拉取完成后再渲染Select组件,保证组件首次挂载时就能拿到完整的defaultValue和匹配的options:
const [request, setRequest] = useState<IRequest>(); const [cities, setCities] = useState<ICity[]>([]); const [defaultCity, setDefaultCity] = useState<{value: string, label: string}>({} as any); // 新增加载状态 const [pageLoading, setPageLoading] = useState(true); useEffect(() => { const initData = async () => { setPageLoading(true) const requestRes = await axios.get<IRequest>(`/api/requests/${id}`) const citiesRes = await axios.get<ICity[]>('/api/cities') setRequest(requestRes.data) setCities(citiesRes.data) setDefaultCity({ value: requestRes.data.city.code, label: requestRes.data.city.name }) setPageLoading(false) } initData() }, []) // 加载过程中显示占位,加载完成再渲染Select {pageLoading ? ( <div>加载中...</div> ) : ( <Select placeholder='City' defaultValue={defaultCity} options={ cities.map(city => ({ value: city.code, label: city.name })) } /> )}
注意:用这个方案要保证
defaultCity的value和options列表里某一项的value完全相等(类型也要一致,比如都是字符串,不要出现一个是数字1一个是字符串"1"的情况),否则react-select匹配不到对应选项,也不会显示选中。
内容的提问来源于stack exchange,提问作者prok05
相关产品推荐
相关产品推荐

