大家好,我卡颂。
创新互联建站总部坐落于成都市区,致力网站建设服务有网站设计、成都网站建设、网络营销策划、网页设计、网站维护、公众号搭建、小程序制作、软件开发等为企业提供一整套的信息化建设解决方案。创造真正意义上的网站建设,为互联网品牌在互动行销领域创造价值而不懈努力!
稍微深入了解过useState的同学都知道 —— useState其实是预置了reducer的useReducer。具体来讲,他预置的reducer实现如下:
function basicStateReducer(state, action) {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
}
那按理来说,useState与useReducer性能应该完全一致才对。但实际上,他们的性能并不一样。本文就来聊聊他们的细微差别。
在v18之前,特定场景下,useReducer存在一个严重的bug。假设我们要挂载如下App组件:
bug复现地址[1]
function App() {
const [disabled, setDisabled] = React.useState(false);
return (
<>
{`Disabled? ${disabled}`}
>
);
}
通过点击按钮,可以切换disabled状态,并将disabled作为props传递给CounterReducer组件。
CounterReducer组件的实现如下:
function CounterReducer({ disabled }) {
const [count, dispatch] = useReducer((state) => {
if (disabled) {
return state;
}
return state + 1;
}, 0);
return (
<>
{`Count ${count}`}
>
);
}
count状态初始为0,当disabled props为true时,点击「reducer + 1按钮」后count不会变化。
disabled为true时,多次点击后count仍显示0
当disabled props为false时,点击「reducer + 1按钮」后count会加1。
disabled为false时,点击后count加1
现在问题来了,当disabled props为true时(此时count为0),我们点击「reducer + 1按钮」5次,然后再点击「Disable按钮」(disabled props会变为false),此时count为多少呢?
按照代码逻辑,改变disabled对count不会造成影响,所以他应该保持原始状态不变(即为0)。
但在v18之前,他会变成5。
但是,如果我们用useState实现同样逻辑的useReducer:
function CounterState({ disabled }) {
const [count, dispatch] = useState(0);
function dispatchAction() {
dispatch((state) => {
if (disabled) {
return state;
}
return state + 1;
});
}
return (
<>
{`Count ${count}`}
>
);
}
就能取得符合预期的效果。
所以说,useReducer的实现在特殊场景下是有bug的(v18之前)。
产生这个bug的原因在于React内部的一种被称为eager state的性能优化策略。
简单的说,对于类似如下这样的,即使多次触发更新,但状态的最终结果不变的情况(在如下例子中count
始终为0):
function App() {
const [count, dispatch] = useState(0);
return ;
}
App组件是没有必要render的。这就省去了render的性能开销。
要命中eager state,有个严格的前提 —— 状态更新前后不变。
我们知道,React中有两种更新状态的方式:
// 定义状态
const [count, dispatch] = useState(0);
// 更新状态
dispatch(100)
// 定义状态
const [count, dispatch] = useState(0);
// 更新状态
dispatch(oldState => oldState + 100)
那么,对于方式1,要保证状态不变很简单,只需要全等比较变化前后的状态,如果他们一致就能进入eager state策略。
对于方式2,就略微复杂点,需要同时满足2个条件:
比如,下述代码就同时满足2个条件,但如果将change放到App内就不满足条件1(App组件每次render时都会创建新的change函数):
// 状态更新函数本身不变
function change(oldState) {
// 新状态也不变
return oldState;
}
function App() {
const [count, dispatch] = useState(0);
// 状态更新函数每次render都会变化
// function change(oldState) {
// 新状态不变
// return oldState;
// }
return ;
}
类似的情况,在useState的实现中,虽然他是预置了reducer的useReducer,但他预置的reducer的引用是不变的,所以用他实现的文章开篇的例子可以命中优化策略。
useReducer在特定场景下的bug就与此相关。并不是说bug产生的原因是useReducer一定没命中优化策略,而是说相比于useState,他命中优化策略很不稳定。
既然bug来源于不稳定的性能优化策略,在没有完美的解决方案之前,React是如何在v18中修复这个bug的呢?
答案是 —— 移除useReducer的eager state策略。也就是说,在任何情况下,useReducer都不再有useState存在的这个性能优化策略了。
这就导致在特定场景下,useReducer的性能弱于useState。
比如在这个v18在线示例[2]中,同样的逻辑用useState实现,不会有冗余的render,而useReducer会有。
在考虑性能优化时,如果useState与useReducer都能满足需要,或许useState是更好的选择。
[1]bug复现地址:https://codesandbox.io/s/vigorous-dhawan-mqv463。
[2]v18在线示例:https://codesandbox.io/s/blazing-cdn-pzcpz6?file=/src/App.js:509-519。
标题名称:UseState与UseReducer性能居然有区别?
链接分享:http://www.shufengxianlan.com/qtweb/news19/160419.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联