React component sometimes renders twice with unchanged state(Reaction组件有时会呈现两次状态不变的情况)
问题描述
我正在使用Redux订阅存储和更新组件。
这是一个没有Redux的简化示例。它使用模拟商店进行订阅和发送。
请按照代码段下面的步骤重现该问题。
编辑:请跳至更新下的第二个演示片段,了解更简洁、更贴近实际的场景。问题是而不是关于Redux。这是关于React的setState函数标识在某些情况下导致重新呈现,即使状态没有更改。
编辑2:在更新2下添加了更简洁的演示。
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="真">const {useState, useEffect} = React;
let counter = 0;
const createStore = () => {
const listeners = [];
const subscribe = (fn) => {
listeners.push(fn);
return () => {
listeners.splice(listeners.indexOf(fn), 1);
};
}
const dispatch = () => {
listeners.forEach(fn => fn());
};
return {dispatch, subscribe};
};
const store = createStore();
function Test() {
const [yes, setYes] = useState('yes');
useEffect(() => {
return store.subscribe(() => {
setYes('yes');
});
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{yes}</h1>
<button onClick={() => {
setYes(yes === 'yes' ? 'no' : 'yes');
}}>Toggle</button>
<button onClick={() => {
store.dispatch();
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QvdW1kL3JlYWN0LmRldmVsb3BtZW50Lmpz"></script>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QtZG9tL3VtZC9yZWFjdC1kb20uZGV2ZWxvcG1lbnQuanM="></script>
发生了什么
- ✅单击"设置为是"。由于
yes的值已为"yes",因此状态不变,因此不会重新呈现组件。 - ✅点击"切换"。
yes设置为"no"。状态已更改,因此将重新呈现该组件。 - ✅单击"设置为是"。
yes设置为"yes"。状态再次更改,于是重新呈现该组件。 - ⛔再次单击"设置为是"。状态未更改,但组件仍在重新呈现。
- ✅后续单击"设置为是"不会导致按预期重新呈现。
预计会发生什么
在步骤4中,不应重新呈现该组件,因为状态未更改。
更新
作为React docs状态,useEffect为
适用于许多常见的副作用,如 订阅和事件处理程序...
其中一个用例可能是侦听浏览器事件,如online和offline。
在本例中,我们在组件首次呈现时调用useEffect内部的函数一次,方法是向其传递一个空数组[]。该函数为联机状态更改设置事件侦听器。
假设,在应用程序的界面中,我们还有一个手动切换在线状态的按钮。
请按照代码段下面的步骤重现该问题。
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="真">const {useState, useEffect} = React;
let counter = 0;
function Test() {
const [online, setOnline] = useState(true);
useEffect(() => {
const onOnline = () => {
setOnline(true);
};
const onOffline = () => {
setOnline(false);
};
window.addEventListener('online', onOnline);
window.addEventListener('offline', onOffline);
return () => {
window.removeEventListener('online', onOnline);
window.removeEventListener('offline', onOffline);
}
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{online ? 'Online' : 'Offline'}</h1>
<button onClick={() => {
setOnline(!online);
}}>Toggle</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QvdW1kL3JlYWN0LmRldmVsb3BtZW50Lmpz"></script>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QtZG9tL3VtZC9yZWFjdC1kb20uZGV2ZWxvcG1lbnQuanM="></script>
发生了什么
- ✅组件首先呈现在屏幕上,并将消息记录在控制台中。
- ✅点击"切换"。
online设置为false。状态已更改,因此将重新呈现该组件。 - ⛔打开开发人员工具,并在网络面板中切换到"脱机"。
online已false,因此状态未更改,但组件仍在重新呈现。
预计会发生什么
在步骤3中,不应重新呈现该组件,因为状态未更改。
更新2
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="真">const {useState, useEffect} = React;
let counterRenderComplete = 0;
let counterRenderStart = 0;
function Test() {
const [yes, setYes] = useState('yes');
console.log(`Component function called ${++counterRenderComplete}`);
useEffect(() => console.log(`Render completed ${++counterRenderStart}`));
return (
<div>
<h1>{yes ? 'yes' : 'no'}</h1>
<button onClick={() => {
setYes(!yes);
}}>Toggle</button>
<button onClick={() => {
setYes('yes');
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QvdW1kL3JlYWN0LmRldmVsb3BtZW50Lmpz"></script>
<script src="aHR0cHM6Ly91bnBrZy5jb20vcmVhY3QtZG9tL3VtZC9yZWFjdC1kb20uZGV2ZWxvcG1lbnQuanM="></script>
发生了什么
- ✅单击"设置为是"。由于
yes的值已经是true,因此状态不变,因此不会重新呈现组件。 - ✅点击"切换"。
yes设置为false。状态已更改,因此将重新呈现该组件。 - ✅单击"设置为是"。
yes设置为true。状态再次更改,于是重新呈现该组件。 - ⛔再次单击"设置为是"。状态未更改,尽管组件通过调用函数启动呈现进程。但是,Reaction会在过程中的某个位置退出呈现,并且不会调用效果。
- ✅后续单击"设置为是"不会按预期重新呈现(函数调用)。
问题
为什么组件仍被重新呈现?是我做错了什么,还是这是一个反应错误?
推荐答案
答案是Reaction使用一组试探法来确定它是否可以避免再次调用呈现函数。这些启发式方法在不同版本之间可能会有所不同,并且不能保证在状态相同的情况下总是能摆脱困境。Reaction提供的唯一保证是如果状态相同,它不会重新呈现子组件。
您的呈现函数应该是纯的。因此,它们运行多少次都无关紧要。如果您要在呈现函数中计算一些开销较大的内容,并担心超出必要地调用它,则可以将该计算包含在useMemo中。
一般来说,在Reaction中"计算呈现数"是没有用的。当确切地反应调用时,您的函数将自行反应,并且确切的时间将在不同版本之间不断变化。这不是合同的一部分。
这篇关于Reaction组件有时会呈现两次状态不变的情况的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:Reaction组件有时会呈现两次状态不变的情况
- Flexslider 箭头未正确显示 2022-01-01
- Quasar 2+Apollo:错误:找不到ID为默认的Apollo客户端。如果您在组件设置之外,请使用ProvideApolloClient() 2022-01-01
- addEventListener 在 IE 11 中不起作用 2022-01-01
- 使用RSelum从网站(报纸档案)中抓取多个网页 2022-09-06
- 失败的 Canvas 360 jquery 插件 2022-01-01
- CSS媒体查询(最大高度)不起作用,但为什么? 2022-01-01
- 400或500级别的HTTP响应 2022-01-01
- Fetch API 如何获取响应体? 2022-01-01
- 如何使用 JSON 格式的 jQuery AJAX 从 .cfm 页面输出查 2022-01-01
- Css:将嵌套元素定位在父元素边界之外一点 2022-09-07
