React Native useEffect异步调用触发警告 如何正确实现座位状态查询功能
问题原因
核心警告触发原因是直接将async函数作为useEffect的回调参数:async函数执行后默认返回Promise对象,而useEffect仅允许回调返回清理函数,因此触发规则警告。
除此之外现有代码还存在隐藏问题:
- 封装的状态逻辑函数未遵循React自定义Hook
use开头的命名规范,可能触发Hook规则校验错误 - useEffect依赖项为空,当seat、vid参数变化时不会重新请求最新的座位状态
- 没有请求中断逻辑,组件卸载后请求完成仍执行setColor会触发内存泄漏警告
直接修复方案
调整useEffect中异步函数的写法,将async函数定义在useEffect内部执行,补充依赖项和清理逻辑即可:
// 自定义Hook遵循use开头的命名约定 const useSeatColor = (seat, vid) => { // 给定默认颜色,避免初始渲染无样式 const [color, setColor] = useState('green'); useEffect(() => { // 声明中断控制器,用于组件卸载时终止未完成的请求 const abortController = new AbortController(); // 异步函数定义在useEffect内部,不直接作为useEffect的回调 const fetchSeatStatus = async () => { try { const response = await fetch( `http://192.168.8.100/dbops/actions/seat_status.php?seat=${seat}&vid=${vid}`, { signal: abortController.signal } ); const result = await response.json(); setColor(result.message); } catch (err) { // 主动中断的请求跳过错误处理 if (err.name !== 'AbortError') { console.error('获取座位状态失败', err); } } }; fetchSeatStatus(); // 返回清理函数,组件卸载时终止请求 return () => abortController.abort(); }, [seat, vid]); // 把参数加入依赖项,参数变化时自动重新请求 return color; }; // 调用方式不变,仅把函数名改为use开头的Hook名 const vid = 4; const seat1Color = useSeatColor(1, vid); const seat2Color = useSeatColor(2, vid); const seat3Color = useSeatColor(3, vid); const seat4Color = useSeatColor(4, vid);
修改后即可消除警告,原有功能不受影响。
更优实现方案
原有方案每个座位单独发起一次请求,20个座位会发起20次请求,性能损耗较高,建议调整为一次性请求所有座位状态,减少网络开销,同时可以避免重复的渲染代码:
const SeatPage = () => { const vid = 4; // 用对象存储所有座位的颜色,key为座位号,value为对应颜色 const [seatColors, setSeatColors] = useState({}); useEffect(() => { const abortController = new AbortController(); const fetchAllSeatStatus = async () => { try { // 调整后端接口,一次性返回当前vid下所有座位的状态,返回格式示例:{1:"red",2:"green",3:"red"...} const response = await fetch( `http://192.168.8.100/dbops/actions/all_seat_status.php?vid=${vid}`, { signal: abortController.signal } ); const result = await response.json(); setSeatColors(result); } catch (err) { if (err.name !== 'AbortError') { console.error('获取所有座位状态失败', err); } } }; fetchAllSeatStatus(); return () => abortController.abort(); }, [vid]); return ( <Screen style={{ paddingBottom: 5 }}> {/* 用循环渲染座位,避免重复代码,20个座位只需调整数组范围即可 */} {[1,2,3,4].map(seatNum => ( <TouchableOpacity key={seatNum} style={styles.seat}> <MaterialCommunityIcons name="seat" size={40} color={seatColors[seatNum] || 'green'} /> <Button title={String(seatNum)} style={{ width: 20 }} color={seatColors[seatNum] || 'green'} /> </TouchableOpacity> ))} <View style={styles.seat}></View> <View style={styles.seat}></View> </Screen> ) }
内容的提问来源于stack exchange,提问作者Julius Peter Odeke
相关产品推荐
相关产品推荐

