Javascript 使用useState对组件渲染进行两次反应
我真的很难弄清楚当没有任何东西被用来触发重新渲染组件时会发生什么Javascript 使用useState对组件渲染进行两次反应,javascript,reactjs,react-router-dom,Javascript,Reactjs,React Router Dom,我真的很难弄清楚当没有任何东西被用来触发重新渲染组件时会发生什么 Events.js当我从Event.js中删除useState()时,组件渲染两次,但我需要保留它。当我在事件组件内部使用useffect()时,第四次渲染 我只是保留了虚拟数据来填补空白,并试图删除React.memo,但什么也没发生。我认为问题在于Event.js组件。我也在使用上下文API,但是第四次渲染太多了 useffect insideApp.js正在从本地存储获取一些值,我无法直接访问该值,因为默认情况下该值未定义
Events.js
当我从Event.js
中删除useState()
时,组件渲染两次,但我需要保留它。当我在事件组件内部使用useffect()
时,第四次渲染
我只是保留了虚拟数据来填补空白,并试图删除React.memo
,但什么也没发生。我认为问题在于Event.js
组件。我也在使用上下文API,但是第四次渲染太多了
useffect insideApp.js
正在从本地存储获取一些值,我无法直接访问该值,因为默认情况下该值未定义
沙盒代码:
Events.js
文件位于/Pages/Events/Events.js
下面是示例代码
Event.js(子组件)
函数事件(){
//状态管理
常量[allEvents,setAllEvents]=React.useState(null);
console.log('Rendering EventsJs',allEvents);
React.useffect(()=>{
setAllEvents(['苹果','香蕉']);
}, []);
返回(
{console.log('Event Rendered.js=>')}
)
}
导出默认的React.memo(事件,(prevProps,nextProps)=>{
返回true;
} );
App.js(父组件)
从'react router dom'导入{BrowserRouter,Route,Redirect};
函数App(){
const[userId,setUserId]=React.useState(null);
React.useffect(()=>{
setUserId(1);
}, []);
//登录
返回(
{console.log('App Rendered')}
);
}
导出默认应用程序;
错误:
事实上,这是由于您使用了
React.memo
,它的第二个参数称为areEqual
,您传入()=>false
,因此您基本上告诉React道具总是在变化。因此,每当应用程序重新启动时,事件也会重新启动。你应该让做出反应。备注检查道具更改。通过传递()=>false
实际上是在告诉你它的道具总是在变化(它们是永远不相等的)
导出默认反应备忘录(事件);
这是一个可行的解决方案。您的应用程序运行良好。它正在按应有的方式渲染。我们知道:
当其道具或状态更改时,React组件将重新渲染
是:
初始道具/状态-->渲染-->DOM更新-->已装载
道具/状态已更改-->渲染-->DOM更新-->已更新。。。诸如此类
在下面的示例中,它渲染了两次,这是正确的:
- 第一个(First console.log)是由于初始呈现状态为
[]
- 第二个(Second console.log)是由于状态更改(由useffect引起)而导致的,
['apple','banana']
去http://localhost:3000,我们看到1个日志:
App Rendered
现在点击“事件”,我们看到3个日志:
1: Event Rendered, [], true
2: Event Rendered, [{}, ... 54 items], true
3: Event Rendered, [{}, ... 54 items], false
这是正确的行为(参考上面写的生命周期顺序):
- 第一个日志:使用初始状态呈现(
[]
,true
)
- 第二个日志:使用新的事件(
54项)和旧的isLoading(true
)进行渲染
- 第三个日志:使用旧的通道(
54项)和新的isLoading(false
)进行渲染
下面是现在要问的正确问题:
问题1:
为什么第二次渲染和第三次渲染(日志)是分开的,它们不应该批处理(合并)并一起应用,因为它们是在同一个函数中编写的吗?
fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})
回答:
否,它们不会在上述代码中批处理。如下所述:
这是实现细节,可能会在将来的版本中更改
在当前版本中,如果您在React事件处理程序中,它们将被批处理在一起。React对React事件处理程序期间完成的所有设置状态进行批处理,并在退出自己的浏览器事件处理程序之前应用这些设置状态
对于当前版本,事件处理程序之外的多个集合状态(例如,在网络响应中)将不会被批处理。因此,在这种情况下,您将获得两个重新渲染
存在强制批处理的临时API。如果您编写ReactDOM.unstable_batchedUpdate(()=>{this.fn1();})代码>则这两个调用都将被批处理。但是我们希望在将来删除这个API,并在默认情况下批处理所有内容
因此,您可以编写(在fetch中),如果需要,它将保存1个渲染:
问题2:
上面引用的React事件处理程序是什么?
fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})
回答:foo
在下面的示例中。这两个设置状态将被批处理
const foo = () => {
setAllEvents([
{ _id: '5ede5af03915bc469a9d598e', title: 'jfklsd', },
])
setLoading(false)
}
<button onClick={foo}>CLICK</button>
constfoo=()=>{
setAllEvents([
{id:'5ede5af03915bc469a9d598e',标题:'jfklsd',},
])
设置加载(错误)
}
点击
问题3:
它更新HTMLDOM的次数是否与呈现的次数相同(打印console.log)?
fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})
回答:否。React在更新真实DOM之前比较计算出的虚拟DOM,因此只有这些更改应用于更新UI所需的真实DOM
问题4:
为什么使用StrictMode
时渲染倍增?
fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})
回答:是的,StrictMode
会有意地双重调用“render”和其他一些生命周期方法来执行。严格的模式检查仅在开发模式下运行;它们不会影响生产构建。当您删除哪个useState
时,为什么要这样使用useffect?您可以通过执行useState(1)
直接将userId设置为1。您是否已启用?效果中的setAllEvents是无意义的,您可以执行React.useState(['apple','banana')代码>并防止装载后重新渲染
ReactDOM.unstable_batchedUpdates(() => {
setAllEvents([...events])
setLoading(false)
})
const foo = () => {
setAllEvents([
{ _id: '5ede5af03915bc469a9d598e', title: 'jfklsd', },
])
setLoading(false)
}
<button onClick={foo}>CLICK</button>