Reactjs React Hooks-useffect即使状态没有更改也会激发

Reactjs React Hooks-useffect即使状态没有更改也会激发,reactjs,react-hooks,Reactjs,React Hooks,我在我的组件中设置了一个效果,如果另一个状态属性发生更改,该效果将更改视图。但是由于某些原因,当组件装载时,即使detailIndex的值没有更改,效果也会运行 const EventsSearchList = () => { const [view, setView] = useState('table'); const [detailIndex, setDetailIndex] = useState(null); useEffect(() => {

我在我的组件中设置了一个效果,如果另一个状态属性发生更改,该效果将更改视图。但是由于某些原因,当组件装载时,即使
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>;

};
const events搜索列表=()=>{
const[view,setView]=useState('table');
const[detailIndex,setDetailIndex]=useState(null);
useffect(()=>{
log('onMount',detailIndex);
//挂载时显示“空”
}, []);
使用效果(
a=>{
console.log('运行效果',详细索引);
//装载时显示“null”!!不应运行。。。
setView(“细节”);
},
[详细索引]
);
返回123;
};
为什么会这样


更新:如果不清楚,我尝试的是在组件更新时运行效果,因为
detailIndex
发生了更改。装载时不会。始终在初始渲染后运行useffect

发件人:

每次渲染后是否运行useEffect?是!默认情况下,它同时运行 在第一次渲染和每次更新之后。(我们稍后再谈 关于如何定制它。)而不是从 “安装”和“更新”,你可能会发现这样想更容易 效果发生在“渲染后”。React保证DOM已被删除 在运行效果时更新

关于您的代码,由于未指定依赖项,以下命令只运行一次(
useffect
将可选的依赖项数组作为第二个参数):

仅当
detailIndex
发生更改时(请尝试在上一个
useffect
中调用
setDetailIndex
):


默认情况下,每个渲染都会执行React钩子中的

useEffect
,但您可以使用函数中的第二个参数定义何时再次执行效果。这意味着函数总是在挂载时执行。在您的情况下,第二个
useffect
将在启动时以及
detailIndex
更改时运行

更多信息:

资料来源:

有经验的JavaScript开发人员可能会注意到,传递给useEffect的函数在每个渲染上都是不同的。[…]如果在重新渲染之间某些值没有更改,您可以告诉React跳过应用效果。为此,将数组作为可选的第二个参数传递给useEffect:[…]


在我的例子中,即使我在useffect()中使用了第二个参数,并且我正在打印该参数以确保它没有更改,也没有更改,组件仍在不断更新。问题是我使用map()渲染组件,在某些情况下,键发生了更改,如果键发生了更改,则它是一个完全不同的对象。

您可以添加第二个保护,检查detailIndex是否已从初始值更改为-1

 useEffect(
    a => {
     if(detailIndex != -1)
      {  console.log('Running effect', detailIndex);
        // On mount shows "null"!! Should not have run...
        setView('detail');
    }},
    [detailIndex]);

如果且仅当
useffect
的第二个参数状态通过引用(而非值)发生更改时,效果才会触发

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;
import React,{useState,useffect}来自“React”;
函数App(){
const[x,setX]=useState({a:1});
useffect(()=>{
console.log('x');
},[x]);
setInterval(()=>setX({a:1}),1000;
返回
}
}
导出默认应用程序;
在这个代码片段中,每秒钟打印一次日志,因为每次调用
setX({a:1})
x
状态都会更新为对新对象的新引用


如果将
setX({a:1})
替换为
setX(1)
,效果将不会定期触发。

一种确保代码仅在“真实”状态更新时运行,并且在第一次渲染时不运行的方法,是指分配一个变量,该变量最初为
false
,在第一次渲染后仅变为
true
,并且该变量自然应为a,因此它将在组件的整个寿命期间被记录

const{useffect,useRef,useState}=React
常量应用=()=>{
const mountedRef=useRef()//← “旗帜”
const[value,setValue]=useState(false)//模拟状态更改
//耍花招
useffect(()=>{
if(mountedRef.current){//← 诀窍
log(“技巧:更改”)
}
},[value])
//不耍花招
useffect(()=>{
console.log(“常规:已更改”)
},[value])
//激发一次并将“mountedRef”ref设置为“true”
useffect(()=>{
console.log(“呈现”)
mountedRef.current=true
//一段时间后更新状态
设置超时(设置值,1000,真)
}, [])
返回空
}
ReactDOM.render(,document.getElementById(“根”))


useffects
总是在mount AFAIK上触发。我的理解是,只有当第二个参数是我认为在重新渲染组件时使用的
[]
时,才会发生这种情况。您想实现什么?@EnriqueMorenoTent否,第二个参数定义何时重新运行此效果,[detailIndex]表示每次detailIndex更改时它都会重新运行([]表示没有重新运行)。这只是一个愚蠢的猜测,但您是否尝试了
useState(未定义)
而不是
useState(null)
。我的第一个想法是,您的状态以前是未定义的,您只需将其设置为null,就可以触发useEffect。但更有可能的是,即使传递了dependecy值,useffect也总是在挂载时执行。保护是唯一的方法吗?对于像ReactAction这样的框架来说,这似乎是非常棘手的问题。
useffect
中的函数是在渲染之后而不是之前运行的。很容易忘记,因为这些定义通常位于函数组件定义的顶部。
 useEffect(
    a => {
     if(detailIndex != -1)
      {  console.log('Running effect', detailIndex);
        // On mount shows "null"!! Should not have run...
        setView('detail');
    }},
    [detailIndex]);
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;