React Native React Navigation v5 聚焦执行函数与收藏列表更新问题
原代码的核心故障点
- 空值判断顺序错误:如果
Fetch_Favorites()返回null,代码会在执行this.tracks_length = favorites.length时直接触发空引用报错,后续的状态更新逻辑完全中断,导致页面卡在旧状态 - 导航事件监听缺少清理逻辑:React Navigation v5的
addListener会返回带remove方法的订阅句柄,原代码没有在组件卸载时移除监听,长期运行会出现监听泄漏、重复触发、事件绑定失效的问题 - 嵌套组件的事件接收不稳定:
Favorite_Songs_Components是被父页面包裹的子组件,没有直接作为导航器的路由挂载目标,依赖透传的navigation对象绑定focus事件时,容易出现事件触发时机偏移的问题 - 渲染判断逻辑耦合混乱:原代码将加载占位、空状态、数据列表的判断逻辑揉在同一个分支里,数据拉取完成后容易因为长度比对不通过,一直卡在占位页无法正常渲染
修正后的实现逻辑
- 调整空值判断顺序,先校验接口返回值有效性,再读取属性、处理数据
- 类组件场景使用官方提供的
withNavigationFocus高阶组件,自动给嵌套组件注入isFocused聚焦状态,替代手动绑定focus监听的写法 - 新增独立的加载状态字段,和业务状态做分离,避免新旧数据交叉渲染
- 简化渲染分支,按加载中、空状态、正常列表的顺序做判断,去掉冗余的长度比对逻辑
修正后的代码示例
父页面Favorites_Songs保持透传必要属性即可:
class Favorites_Songs extends React.Component { render() { return ( <Favorite_Songs_Components tracks={this.props.tracks} navigation={this.props.navigation} /> ) } }
核心组件Favorite_Songs_Components调整为:
// 引入官方高阶组件 import { withNavigationFocus } from '@react-navigation/core'; class Favorite_Songs_Components extends React.Component { state = { favorite_songs: "", isLoading: true } componentDidMount() { // 首次挂载页面时主动拉取一次数据 this.get_favorites(); } componentDidUpdate(prevProps) { // 仅当页面从失焦状态切换为聚焦状态时,重新拉取最新收藏数据 if (!prevProps.isFocused && this.props.isFocused) { this.get_favorites(); } } get_favorites = async() => { this.setState({ isLoading: true }); try { const allAvailableTracks = [...(this.props?.tracks || [])]; const favorites = await Fetch_Favorites(); // 接口返回空值时,直接进入空状态 if (!favorites) { this.setState({ favorite_songs: "empty", isLoading: false }); return; } // 过滤出匹配收藏列表的歌曲,适配对象/字符串两种格式的收藏数据 const matchedFavorites = allAvailableTracks.filter(track => favorites.some(favItem => typeof favItem === 'string' ? favItem === track.title : favItem.title === track.title ) ); // 过滤后无匹配结果,进入空状态 if (matchedFavorites.length === 0) { this.setState({ favorite_songs: "empty", isLoading: false }); return; } this.setState({ favorite_songs: matchedFavorites, isLoading: false }); } catch (err) { // 接口异常时兜底走空状态,避免页面卡死 this.setState({ favorite_songs: "empty", isLoading: false }); } } render() { // 加载中展示占位 if (this.state.isLoading) { return <Text>加载中...</Text> } // 空收藏状态展示提示 if (this.state.favorite_songs === "empty") { return ( <View style={this.style.container}> <Text style={this.style.favorites}>No favorites songs.</Text> </View> ) } // 正常渲染歌曲列表 const renderProps = { route: "favorites", tracks: this.state.favorite_songs, navigation: this.props.navigation, } return <Tracks {...renderProps} /> } } // 高阶组件包裹后,组件会自动获得isFocused属性,且自动处理监听的绑定与清理 export default withNavigationFocus(Favorite_Songs_Components);
实现注意事项
- 不需要再手动调用
navigation.addListener("focus")绑定事件,withNavigationFocus会在组件生命周期内自动完成监听注册、卸载清理的全流程,不会出现内存泄漏 - 每次触发数据拉取时先将
isLoading设为true,避免旧数据和新拉取的数据交叉渲染导致的显示异常 - 所有异步接口请求都要做异常兜底,避免接口返回异常时页面白屏、状态永久卡住
- 嵌套在屏幕内部的非路由组件,不要直接依赖透传的navigation对象绑定导航事件,通过官方高阶组件注入的聚焦状态是React Navigation v5类组件场景下的标准适配方案
内容的提问来源于stack exchange,提问作者AC_Valhalla
相关产品推荐
相关产品推荐

