在 React / Redux 中了解 UI 何时准备就绪
Posted
技术标签:
【中文标题】在 React / Redux 中了解 UI 何时准备就绪【英文标题】:Knowing when the UI is ready in React / Redux 【发布时间】:2018-03-06 22:17:51 【问题描述】:我想知道我的应用需要多长时间才能“准备好”让用户与之交互。应用加载的时间线包括以下内容:
-
DOM 加载。
初始 React 渲染。
从各种
componentDidMount()
s 触发的 HTTP 调用
HTTP 调用返回。 Redux 状态使用 HTTP 响应进行更新。
React 使用来自 HTTP 响应的新值呈现。
所有初始加载已完成。用户已准备好与应用交互。
在这个过程结束时,我想触发一个跟踪事件来记录window.performance.now()
的值。
我不确定最好的方法是什么。 React 的组件系统很好地解耦了 UI 的各个部分。在这种情况下,不幸的是,这意味着我不会有一个简单的地方来检查“是否所有内容都已使用新数据呈现”。
我尝试过或考虑过的事情:
-
寻找一个 React 生命周期钩子,它告诉我是否有任何孩子正在更新。像
componentOrAnyChildDidUpdate()
这样的东西。我认为这不存在,并且可能与 React 哲学背道而驰。
通过context
将anyChildDidUpdate()
生命周期钩子组合在一起,并使我的应用程序中的每个组件都成为辅助抽象基类的子类或包装在更高阶的组件中。这似乎很糟糕,因为 context
是 experimental API。
通过store.subscribe()
订阅 Redux 状态。更新 Redux 状态以明确记录是否所有 HTTP 调用都已返回。一旦所有的 HTTP 调用都返回了,并且 React 完成了重新渲染,那么我就知道要触发跟踪回调了。问题是知道 React 何时完成重新渲染。如果我的store.subscribe()
回调保证在react-redux
的回调之后同步执行,它将起作用。但事实并非如此。
在 React 中有没有好的方法来做到这一点?
【问题讨论】:
为什么不在一个 redux "APP_START" 操作中包含所有这些 API 调用? 一个 redux 操作本身是不够的。我想知道应用何时完成渲染以响应 redux 操作。 @NickHeiner 您可以在您的 redux 状态中跟踪所有自包含静态组件的列表,然后一旦您使用 3. 确定状态最终确定的点,您就等待每个跟踪的组件发送一个指示渲染完成的 redux 操作。 @NickHeiner 您是否使用任何 redux 中间件来捕获 HTTP 调用?比如redux-promise
、redux-saga
等等?
【参考方案1】:
恐怕在 React 中没有通用的好方法,这是与应用程序结构相关的“逻辑”。
我想在用 react 编写的单页应用程序(SPA)中从一个页面导航到另一个页面时显示一个加载器,但我遇到了类似的问题,不知道何时停止显示它以及新页面中的所有组件是否已完成他们的 API 调用/渲染。
我的解决方法是:
1) 我从组件内部删除了所有 API 调用。
2) 创建一个页面组件来包装该页面中包含的所有组件(由您的反应路由器调用)。
3) 在导航时同时执行组件所需的所有请求(在我的例子中是在显示加载程序时)。对于每个完成的请求,创建一个 Promise 并在其中使用 React.createElement 动态创建您的组件。将创建请求响应和承诺处理程序作为道具传递给组件。
4) 解决组件componentDidMount
中的承诺。
5) 一旦所有的承诺都解决了,你就知道“页面”已经准备好了,你可以记录window.performance.now()
的值。
很难提供一个没有大量上下文代码的最小代码示例,因为这些代码分布在整个应用程序中。
【讨论】:
> 恐怕在 React 中没有一种通用的好方法 - 在 React 中 - 没有。在 redux 中 - 可能。在惯用的 redux 中 - 很容易。 Redux 在 OP 的问题中给出。【参考方案2】:有不止一种方法可以满足您的要求 - 但我想我会做以下事情:
• 创建一个 perf reducer 并在我创建 redux 存储并将值添加到初始状态之前调用 performance.now()
。
perf: start: '...', end: '...'
• 跟踪加载的 reducer 中初始 HTTP 请求的加载状态。
loaded: request1: false, request2: false, request3: false
• 将***组件连接到加载的reducer 并检查componentDidUpdate 中的所有请求是否完成。如果为 true,则将 end 值添加到 perf reducer。
import React from 'react';
import connect from 'react-redux';
class App extends React.Component
componentDidUpdate(prevProps)
if (!prevProps.loadingComplete && this.props.loadingComplete)
this.props.updatePerf(performance.now());
const mapStateToProps = state => (
loadingComplete: state.loaded.every(item => item),
);
const mapDispatchToProps = dispatch => (
updatePerf(time)
dispatch( type: 'SET_ENDING_PERF', payload: time );
,
);
export default connect(mapStateToProps, mapDispatchToProps)(App);
【讨论】:
【参考方案3】:在我们的项目中,我们使用了 redux 和一些操作 - 触发器、请求、成功、错误。
“触发器”调用 request 并抛出 loading: true 到我们的“智能”组件中 “请求”获取数据并将它们放入成功/错误中 “成功”,告诉我们一切正常,数据加载完毕并抛出 loading: false, data
在成功/错误时,我们总是知道我们的应用程序正在发生这种情况
当然,您可以使用 componentWillReceiveProps(nextProps) 并检查组件中的加载标志和数据
【讨论】:
【参考方案4】:你可以尝试使用react-addons-perf
这就是我通常对我的应用进行基准测试的方式:
基本上,您希望在组件开始更新时开始测量,并在生命周期方法 componentDidUpdate()
之后停止测量。
import Perf from 'react-addons-perf'
componentWillMount()
window.performance.mark('My app');
componentDidMount()
console.log(window.performance.now('My app'));
Perf.start()
// ... do your HTTP requests
componentDidUpdate()
Perf.stop();
Perf.printInclusive();
就像您将跟踪组件初始渲染 + 组件更新一样。
希望对你有帮助
【讨论】:
不要使用 react-addons-perf,因为它已被弃用并且不再适用于最新版本的 react(即 16.0.0)。 如果你使用 React 16 是正确的。如果你不打算升级(即使你应该升级)仍然有效。好收获!【参考方案5】:实现这一目标的两个步骤。
通过操作跟踪所有 HTTP 请求
简单的计数器处于redux状态就足够了,它在请求开始时递增,在成功或失败时递减(所有通过适当的操作)。
没有window.dirty.stuff
在 Dan Abramov 面前。
跟踪所有 connect
ed 组件
componentDidUpdate
方法表示所有子级都完成了渲染。这可以通过recompose
的lifecycle
HOC 实现,无需太多样板。开始跟踪重新渲染的正确位置可能是 sCU
方法或只是 cWRP
如果您已经使用 recompose
s shouldUpdate
或 reselect
- 因为我们不会跟踪未更新的组件。
请注意,所有这些都是假设惯用的 react/redux 应用程序,没有棘手的副作用或动画。例如,如果层次结构深处的某个子组件触发其自己的 AJAX 请求,例如在cDM
上,而不是通过 redux 操作。这样你也必须跟踪它们,但没有人能确定它们是否意味着任何重新渲染。
跟踪此类行为仍然是可能的,只是可能需要更多的努力。
【讨论】:
【参考方案6】:从设计 POV 来看,在我看来,准备好与否是用户呈现属性,因此应该由 react 组件单独处理。
例如,您可能稍后决定允许用户与 UI 的某些部分进行交互,而其他部分仍在加载,或者您可能决定某些组件应允许用户在数据模型准备好之前进行交互,等等......换句话说,它是一个(反应)组件的责任来判断它是否/何时准备好而不是其他人。
也就是说,所有 'waitable' 组件都可以暴露一个类似 onLoad 的 prop 以向上传递到反应树;然后,根组件将通过将适当的道具向下传播到叶子来相应地更改树的下游来协调结果。
这可以通过编写一个 mixin/high order 组件来封装“就绪更新”逻辑来实现。
【讨论】:
以上是关于在 React / Redux 中了解 UI 何时准备就绪的主要内容,如果未能解决你的问题,请参考以下文章
如何使用react-redux将redux商店中封装的ui状态映射到道具?
React、Redux、React-Router - 卡在安装 material-ui
手写一个React-Redux,玩转React的Context API