React Native/Redux 应用程序中可能存在的导航问题
Posted
技术标签:
【中文标题】React Native/Redux 应用程序中可能存在的导航问题【英文标题】:Possible navigation issue in React Native/Redux app 【发布时间】:2016-12-08 06:43:46 【问题描述】:在大型 React Native 应用程序中使用 Redux 进行导航期间,所有访问的场景(来自导航堆栈的场景)都保持挂载状态。当从最后一个场景组件发送任何动作时,所有这些场景都会接收道具并按照访问顺序进行渲染。它会导致调度和最后一个场景渲染之间的冻结和可见延迟。
对于导航,我使用的是react-native-router-flux,但原来的 React Native Navigator 也会出现同样的问题。
视频Possible navigation issues in React Native/Redux app
代码react-redux-navigation-test
很高兴知道如何防止将道具从导航链传递给未聚焦的组件。
目前,我正在检查每个组件的 shouldComponentUpdate 是否已聚焦(可见),并在相反的情况下返回 false。
有没有更好的解决方案?
【问题讨论】:
嗨!我的 RN 应用程序也有同样的问题。有没有找到解决办法? 【参考方案1】:我建议您使用场景中的导航索引和标题渲染方法计算当前场景和正在渲染的场景之间的场景距离。如果距离 > 1 返回 null。
这应该会阻止呈现整个导航历史记录。对我来说,这种行为不是开箱即用的,但它确实存在。
【讨论】:
如果您提供一些示例代码sn-ps会很棒。在哪里查看距离?等【参考方案2】:我没有使用过 react-native-router-flux,但是我确实有使用过相当大的 React Native 和 Redux 应用程序的经验。我注意到,有时如果您正在使用的数据变得足够大,它可能会导致明显的延迟,但这些大多仅限于开发工作。构建应用程序时,它通常会明显更快。似乎 Redux 在开发模式和生产模式中的做法略有不同。
关于仍在安装的导航堆栈中的场景,这是设计使然。即使您 navigator.push
到另一个屏幕时,这些先前的屏幕仍会保持安装状态,直到它们从 currentRouteStack
弹出或从 currentRouteStack
替换。
另外,值得注意的是您的模拟器处于慢动画模式。不确定您是否为了视频而这样做,但导航速度慢部分是由于这个原因。只是提醒一下,以防这不是故意的。
在您构建应用程序之后,我会检查它的运行情况,并且在进一步解决性能问题之前它不处于开发模式。对于最终的应用产品来说可能不是问题。
【讨论】:
感谢您这么快的答复。此问题也发生在生产模式下的真实设备上。这些延迟主要取决于导航堆栈中的场景有多大。在上面的示例中,在开始场景中显示了 1000 个元素的巨大列表,以强调这些延迟。在我正在处理的实际应用程序中,这些延迟并没有那么大,但很重要。视频示例中场景之间的过渡时间为 2 秒。不是慢速模式。因此,我正在寻找一种更好的方法来防止渲染甚至道具通过不可见的场景。目前我正在使用检查 shouldComponentUpdate 嗯,很有趣。我看到您正在将这些元素呈现到 ScrollView 中。我很想知道 ListView 会对性能产生什么影响。我相信他们使用其他性能改进来呈现 ListView 行。可能值得研究。【参考方案3】:我打算在这里冒险,但您是否使用以下方法之一来防止重新渲染?
PureComponents(我认为是在 React 15.3 中添加的) 使用 shouldComponentUpdate 方法手动使用浅比较道具和状态。默认情况下,React 会在更新时重新渲染所有组件,除非你正确处理了 shouldComponentUpdate。
我正在做类似的事情,根本没有这些问题
【讨论】:
【参考方案4】:我们通过简单地将所有屏幕包装在一个组件中解决了这个问题。
我们有一个包裹整个屏幕的 ScreenView 组件,我们向它传递了一个参数“restrictRerendersToRoutes”。
这个屏幕连接到状态,我们更新我们状态中的 currentScrene 并将它暴露给这个屏幕视图。
然后我们通过这个 shouldComponentUpdate 实现简单地限制屏幕在后台时的重新渲染:
shouldComponentUpdate(nextProps)
if (!_.empty(this.props.restrictRerendersToRoutes))
return !!this.props.restrictRerendersToRoutes
.find((routeKey) => routeKey === nextProps.currentScene.name);
return true;
【讨论】:
您能否提供一个更详细的示例,这将非常有帮助。谢谢!【参考方案5】:问题确实是整个导航堆栈都连接到了商店(因为除非您使用resetto或resetnavigation,否则不会卸载组件)
因为这里的价值就是我现在所做的。 我一直在使用稍微修改过的 react redux 实现,它跳过了不在视图中的场景的更新(见下文)
要使用它,您需要:
在上下文中存储一个组件树的路由
我们为此创建了一个包装器
const CurrentRoute = React.createClass(
childContextTypes : currentRoute: PropTypes.object ,
getChildContext()
return currentRoute: this.props.route
,
render() return this.props.children;
)
并在渲染场景中使用它
<CurrentRoute route=route><CurrentScene navigate=navigate route=route index=index /></CurrentRoute>
然后你就可以访问组件被渲染到的路由了。
将导航堆栈存储在单例中 我们在配置场景中使用此代码
let routeStack = [];
export const updateRouteStack = stack => routeStack = stack;
然后你可以使用这个稍微修改过的 react-redux 连接函数,如果组件在另一个组件树中渲染然后当前显示的那个,它将跳过更新 (或类似的实现) https://github.com/reactjs/react-redux/compare/master...ganmor:master
也许可以打包这个,但我没有时间研究它。如果有人愿意这样做.. 希望有帮助
【讨论】:
以上是关于React Native/Redux 应用程序中可能存在的导航问题的主要内容,如果未能解决你的问题,请参考以下文章
React-Native + Redux 将 ID 传递给组件
React 应用程序转换为 react-native - react-redux 和 react-thunk 的问题
我在 react-native 和 redux 中处理 API 调用的地方