我在组件内部设置了一个效果,如果另一个状态属性发生变化,该效果也会改变视图。但是由于某种原因,即使detailIndex
的值没有更改,安装组件时也会运行效果。
const EventsSearchList = () => {
const [view, setView] = useState('table');
const [detailIndex, setDetailIndex] = useState(null);
useEffect(() => {
console.log('onMount', detailIndex);
// On mount shows "null"
}, []);
useEffect(
a => {
console.log('Running effect', detailIndex);
// On mount shows "null"!! Should not have run...
setView('detail');
},
[detailIndex]
);
return <div>123</div>;
};
为什么会这样?
更新:如果不清楚,我要尝试的是在组件更新时运行效果,因为detailIndex
发生了变化。挂载时不行。
答案 0 :(得分:7)
就我而言,即使我在useEffect()中使用了第二个参数,并且该组件仍在打印,以确保其不变和不变,该组件仍在不断更新。问题是我正在使用map()渲染组件,并且在某些情况下键发生了更改,如果键发生更改,则它是一个完全不同的对象。
答案 1 :(得分:3)
useEffect
在每个渲染上都执行,但是您可以在函数中使用第二个参数来定义何时再次执行效果。这意味着该功能在安装时始终始终执行。根据您的情况,第二个useEffect
将在启动时以及detailIndex
更改时运行。
更多信息:https://reactjs.org/docs/hooks-effect.html
来源:
经验丰富的JavaScript开发人员可能会注意到,传递给useEffect的函数在每个渲染器上都会有所不同。 [...]如果在重新渲染之间某些值没有更改,您可以告诉React跳过应用效果。为此,请将数组作为第二个可选参数传递给useEffect:[...]
答案 2 :(得分:3)
您可以添加第二个防护,检查detailIndex是否已从初始值更改为-1
useEffect(
a => {
if(detailIndex != -1)
{ console.log('Running effect', detailIndex);
// On mount shows "null"!! Should not have run...
setView('detail');
}},
[detailIndex]);
答案 3 :(得分:1)
确保代码仅在“真实” 状态更新上运行并且不能也在首次渲染上运行的一种方法是,分配一个初始为{{1} },并且仅在第一个渲染后变为false
,并且该变量自然应该是hook,因此它将在组件的寿命。
true
const {useEffect, useRef, useState} = React
const App = () => {
const mountedRef = useRef() // ← the "flag"
const [value, setValue] = useState(false) // simulate a state change
// with the trick
useEffect(() => {
if (mountedRef.current){ // ← the trick
console.log("trick: changed")
}
}, [value])
// without the trick
useEffect(() => {
console.log("regular: changed")
}, [value])
// fires once & sets "mountedRef" ref to "true"
useEffect(() => {
console.log("rendered")
mountedRef.current = true
// update the state after some time
setTimeout(setValue, 1000, true)
}, [])
return null
}
ReactDOM.render(<App/>, document.getElementById("root"))
答案 4 :(得分:0)
useEffect
总是在初始渲染后运行。
来自docs:
useEffect是否在每次渲染后运行?是的!默认情况下,它同时运行 在第一个渲染之后和每次更新之后。 (我们稍后再谈 关于如何自定义它。)而不是按照 “安装”和“更新”,您可能会发现更容易想到 效果发生在“渲染后”。 React保证DOM已经存在 在效果运行时更新。
关于您的代码,以下代码将仅运行一次(因为您提供了一个空数组作为useEffect
的第二个参数):
useEffect(() => {
console.log('onMount', detailIndex);
// On mount shows "null" -> since default value for detailIndex is null as seen above
// const [detailIndex, setDetailIndex] = useState(null);
}, []);
只有在detailIndex
发生更改时(尝试在先前的setDetailIndex
中调用useEffect
),此命令才会运行:
useEffect(() =>
// ... effect code
}, [detailIndex]);
答案 5 :(得分:0)
并且仅当mode='reschedule'
的第二个参数状态通过引用(而不是值)更改时,才会触发效果。
useEffect
在此代码段中,日志每秒钟打印一次,因为每次调用import React, { useState, useEffect } from 'react';
function App() {
const [x, setX] = useState({ a: 1 });
useEffect(() => {
console.log('x');
}, [x]);
setInterval(() => setX({ a: 1 }), 1000);
return <div></div>
}
}
export default App;
时,setX({a: 1})
状态都会用对新对象的新引用进行更新。
如果将x
替换为setX({a: 1})
,效果将不会定期触发。