图文示例讲解useState与useReducer性能区别
|
引言
稍微深入了解过的同学都知道 ——其实是预置了的。具体来讲,他预置的实现如下:- function basicStateReducer(state, action) {
- // $FlowFixMe: Flow doesn't like mixed types
- return typeof action === 'function' ? action(state) : action;
- }
复制代码 那按理来说,与性能应该完全一致才对。但实际上,他们的性能并不一样。本文就来聊聊他们的细微差别。
一个严重的bug
在之前,特定场景下,存在一个严重的。假设我们要挂载如下组件:- function App() {
- const [disabled, setDisabled] = React.useState(false);
- return (
- <>
- <button onClick={() => setDisabled((prev) => !prev)}>Disable</button>
- <div>{`Disabled? ${disabled}`}</div>
- <CounterReducer disabled={disabled} />
- </>
- );
- }
复制代码 通过点击按钮,可以切换状态,并将作为传递给组件。组件的实现如下:- function CounterReducer({ disabled }) {
- const [count, dispatch] = useReducer((state) => {
- if (disabled) {
- return state;
- }
- return state + 1;
- }, 0);
- return (
- <>
- <button onClick={dispatch}>reducer + 1</button>
- <div>{`Count ${count}`}</div>
- </>
- );
- }
复制代码 状态初始为0,当为时,点击reducer + 1按钮后不会变化。
当为时,点击reducer + 1按钮后会加1。
现在问题来了,当为时(此时为0),我们点击reducer + 1按钮5次,然后再点击Disable按钮(会变为),此时为多少呢?
按照代码逻辑,改变对不会造成影响,所以他应该保持原始状态不变(即为0)。
但在之前,他会变成5。
但是,如果我们用实现同样逻辑的:- function CounterState({ disabled }) {
- const [count, dispatch] = useState(0);
- function dispatchAction() {
- dispatch((state) => {
- if (disabled) {
- return state;
- }
- return state + 1;
- });
- }
- return (
- <>
- <button onClick={dispatchAction}>state + 1</button>
- <div>{`Count ${count}`}</div>
- </>
- );
- }
复制代码 就能取得符合预期的效果。
所以说,的实现在特殊场景下是有的(v18之前)。
bug是如何产生的
产生这个的原因在于内部的一种被称为的性能优化策略。
简单的说,对于类似如下这样的,即使多次触发更新,但状态的最终结果不变的情况(在如下例子中始终为0):- function App() {
- const [count, dispatch] = useState(0);
- return <button onClick={() => dispatch(0)}>点击</button>;
- }
复制代码 组件是没有必要的。这就省去了的性能开销。
要命中,有个严格的前提 —— 状态更新前后不变。
我们知道,中有两种更新状态的方式:
- // 定义状态
- const [count, dispatch] = useState(0);
- // 更新状态
- dispatch(100)
复制代码- // 定义状态
- const [count, dispatch] = useState(0);
- // 更新状态
- dispatch(oldState => oldState + 100)
复制代码 那么,对于方式1,要保证状态不变很简单,只需要全等比较变化前后的状态,如果他们一致就能进入策略。
对于方式2,就略微复杂点,需要同时满足2个条件:
- 状态更新函数本身不变
- 通过状态更新函数计算出的新状态也不变
比如,下述代码就同时满足2个条件,但如果将放到内就不满足条件1(组件每次时都会创建新的函数):- // 状态更新函数本身不变
- function change(oldState) {
- // 新状态也不变
- return oldState;
- }
- function App() {
- const [count, dispatch] = useState(0);
- // 状态更新函数每次render都会变化
- // function change(oldState) {
- // 新状态不变
- // return oldState;
- // }
- return <button onClick={() => dispatch(change)}>点击</button>;
- }
复制代码 类似的情况,在的实现中,虽然他是预置了的,但他预置的的引用是不变的,所以用他实现的文章开篇的例子可以命中优化策略。在特定场景下的就与此相关。并不是说产生的原因是一定没命中优化策略,而是说相比于,他命中优化策略很不稳定。
v18之后的改变
既然来源于不稳定的性能优化策略,在没有完美的解决方案之前,是如何在中修复这个的呢?
答案是 —— 移除的策略。也就是说,在任何情况下,都不再有存在的这个性能优化策略了。
这就导致在特定场景下,的性能弱于。
比如在v18在线示例中,同样的逻辑用实现,不会有冗余的,而会有。
总结
在考虑性能优化时,如果与都能满足需要,或许是更好的选择。
以上就是useState与useReducer性能区别图文示例详解的详细内容,更多关于useState useReducer性能区别的资料请关注脚本之家其它相关文章!
来源:https://www.jb51.net/javascript/2844414oy.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|
|
|
发表于 2023-5-17 10:59:10
举报
回复
分享
|
|
|
|