React Native useEffect逻辑错误优化求助:fetchData提前调用问题
修复useEffect触发时机错误导致fetchData拿到null参数的问题
你的核心问题在于useEffect的依赖配置错误,加上React状态更新的异步特性,导致fetchData在apiToken和userId还没拿到有效值时就被调用了。下面是具体的问题分析和优化后的代码:
问题根源
- 组件初始渲染时,
apiToken和userId都是null,此时useEffect因为依赖这两个值会立即触发,调用getData后立刻通过.then()执行fetchData——但setApiToken和setUserId是异步操作,状态还没更新,所以fetchData里拿到的还是初始的null值。 - 当
getData完成并更新apiToken和userId后,useEffect又会因为依赖变化再次触发,导致getData被重复调用,陷入不必要的循环。
优化后的代码
export const SitesScreen = ({navigation}) => { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [apiToken, setApiToken] = useState(null); const [userId, setUserId] = useState(null); // 重构fetchData:添加参数校验,统一用async/await写法,增加错误捕获 const fetchData = async (token, id) => { // 先校验参数是否有效,无效直接返回 if (!token || !id) { setLoading(false); setIsRefreshing(false); return; } setIsRefreshing(true); try { const response = await fetch( `${global.endpoint}/sites/current-user-sites/${id}?access-token=${token}`, { method: 'GET' } ); const responseJson = await response.json(); if (responseJson.success) { setData(responseJson.data); console.log(responseJson); } } catch (error) { console.error('请求失败:', error); // 这里可以添加错误提示逻辑,比如Toast提示用户 } finally { setLoading(false); setIsRefreshing(false); } }; const getData = async () => { try { const deviceDataStr = await AsyncStorage.getItem('device_data'); const deviceData = JSON.parse(deviceDataStr); const { access_token: token, id } = deviceData; setApiToken(token); setUserId(id); // 直接把拿到的token和id传给fetchData,不用等状态更新 await fetchData(token, id); } catch (error) { console.error('获取本地数据失败:', error); setLoading(false); } }; // 只在组件挂载时执行一次getData,不需要依赖apiToken和userId useEffect(() => { getData(); }, []); const renderItem = ({ item, index, }: { item: IListItem, index: number, }): React.ReactElement => ( <ListItem title={`${item.name}`} description={`${item.address}, ${item.number} - ${item.city}`} onPress={() => navigation.navigate('Details', { siteId: item.id, }) } /> ); // 下拉刷新时,用已有的状态值调用fetchData const handleRefresh = () => { fetchData(apiToken, userId); }; return ( <SafeAreaView style={GlobalStyles.safeArea}> <TopNavigation title={evaProps => ( <Text style={GlobalStyles.headerTitle}>Cantieri</Text> )} alignment="center" /> <Divider /> <FlatList data={data} refreshControl={ <RefreshControl refreshing={isRefreshing} onRefresh={handleRefresh} /> } ItemSeparatorComponent={Divider} renderItem={renderItem} /> </SafeAreaView> ); };
关键改动说明
- 调整useEffect触发时机:把依赖数组改为空数组
[],让getData只在组件首次挂载时执行一次,避免重复调用和循环触发。 - fetchData参数直接传递:在
getData拿到deviceData后,直接把token和id传给fetchData,绕开React状态更新的异步延迟,确保请求拿到的是有效值。 - 添加参数校验:在
fetchData开头先检查token和id是否存在,防止无效请求。 - 统一异步写法:把原来混合的
async/await和.then()改成纯async/await,代码更易读,同时添加try/catch捕获请求和解析错误,避免程序崩溃。 - 独立下拉刷新逻辑:单独写
handleRefresh函数,下拉时用已保存的apiToken和userId调用fetchData,确保刷新时参数有效。
内容的提问来源于stack exchange,提问作者user3348353
相关产品推荐
相关产品推荐

