Reactjs 使用useEffect,如何跳过在初始渲染时应用效果?

Reactjs 使用useEffect,如何跳过在初始渲染时应用效果?,reactjs,react-hooks,Reactjs,React Hooks,使用React的新效果挂钩,我可以告诉React,如果在重新渲染之间某些值没有更改,则可以跳过应用效果-React文档中的示例: useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Only re-run the effect if count changes 但上面的示例在初始渲染时应用效果,并在count已更改的后续重新渲染时应用效果。如何告诉React跳过初始渲染的效果

使用React的新效果挂钩,我可以告诉React,如果在重新渲染之间某些值没有更改,则可以跳过应用效果-React文档中的示例:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
但上面的示例在初始渲染时应用效果,并在
count
已更改的后续重新渲染时应用效果。如何告诉React跳过初始渲染的效果?

如指南所述

效果挂钩useEffect增加了从功能组件执行副作用的能力。它与React类中的componentDidMount、componentDidUpdate和componentWillUnmount的作用相同,但统一为一个API

在本指南的示例中,仅在初始渲染时,
count
应为0:

const [count, setCount] = useState(0);
因此,它将作为
componentdiddupdate
工作,并进行额外检查:

useEffect(() => {
  if (count)
    document.title = `You clicked ${count} times`;
}, [count]);
这就是可以用来代替
useffect
的自定义钩子的工作原理:

function useDidUpdateEffect(fn, inputs) {
  const didMountRef = useRef(false);

  useEffect(() => {
    if (didMountRef.current)
      fn();
    else
      didMountRef.current = true;
  }, inputs);
}

由于@Tholle建议使用
useRef
而不是
setState
,我使用常规状态变量而不是ref,因此获得了积分

//将didMount初始化为false
const[didMount,setDidMount]=useState(false)
//安装时将didMount设置为true
useffect(()=>{setDidMount(true)},[]
//现在我们有了一个变量,它告诉我们组件是否
//我们可以基于此更改其他效果的行为
const[count,setCount]=useState(0)
useffect(()=>{
if(didMount)document.title=`您单击了${count}次`
},[计数])
我们可以像这样将didMount逻辑重构为一个定制钩子

函数useDidMount(){
const[didMount,setDidMount]=useState(false)
useffect(()=>{setDidMount(true)},[]
返回迪德蒙特
}
最后,我们可以像这样在我们的组件中使用它

const didMount=useDidMount()
const[count,setCount]=useState(0)
useffect(()=>{
if(didMount)document.title=`您单击了${count}次`
},[计数])

更新使用useRef hook避免额外的重新加载(感谢@TomEsterez的建议)

这一次,我们的自定义钩子返回一个函数,返回ref的当前值。你也可以直接使用裁判,但我更喜欢这个

函数useDidMount(){
const mountRef=useRef(false);
useffect(()=>{mountRef.current=true},[]);
return()=>mountRef.current;
}
用法

constmycomponent=()=>{
const didMount=useDidMount();
useffect(()=>{
if(didMount())//做点什么
做点别的
})
返回(
某物
);
}

顺便说一句,我从来没有使用过这个钩子,可能有更好的方法来处理这个问题,这更符合React编程模型的思路。

这里有一个自定义钩子,它只提供一个布尔标志来指示当前渲染是否是第一个渲染(当安装组件时)。这与其他一些答案大致相同,但您可以在
useffect
或渲染函数中使用该标志,或在组件中的任何其他位置使用该标志。也许有人能提出一个更好的名字

从'react'导入{useRef,useffect};
导出常量useIsMount=()=>{
const isMountRef=useRef(真);
useffect(()=>{
isMountRef.current=假;
}, []);
返回isMountRef.current;
};
您可以像这样使用它:

import React,{useffect}来自“React”;
从“/useIsMount”导入{useIsMount};
常量MyComponent=()=>{
const isMount=useIsMount();
useffect(()=>{
if(isMount){
console.log('First Render');
}否则{
console.log(“后续渲染”);
}
});
返回isMount?第一次渲染

:后续渲染

; };
如果您感兴趣,这里有一个测试:

从'@testing library/react hooks'导入{renderHook};
从“../useIsMount”导入{useIsMount};
描述('useIsMount',()=>{
它('在第一次渲染时应为true,在',()=>{
const{result,rerender}=renderHook(()=>useIsMount());
expect(result.current)、toEqual(true);
重新加载();
expect(result.current).toEqual(false);
重新加载();
expect(result.current).toEqual(false);
});
});

我们的用例是隐藏动画元素,如果初始道具指示它们应该隐藏。在以后的渲染中,如果道具发生了变化,我们确实希望元素能够动画化。

我找到了一个更简单的解决方案,不需要使用另一个钩子,但它有缺点

useEffect(() => {
  // skip initial render
  return () => {
    // do something with dependency
  }
}, [dependency])
这只是一个例子,如果您的案例非常简单,那么还有其他方法可以做到这一点

这样做的缺点是不能产生清理效果,只能在依赖项数组第二次更改时执行

这是不推荐使用的,你应该使用其他答案所说的,但我只是在这里添加了这一点,让人们知道有不止一种方法可以做到这一点

编辑: 更清楚地说,您不应该使用此方法来解决问题中的问题(跳过初始渲染),这仅用于教学目的,表明您可以用不同的方式做相同的事情。
如果您需要跳过初始渲染,请在其他答案上使用该方法。

一个类型脚本和CRA友好的挂钩,将其替换为
useffect
,该挂钩的工作原理类似于
useffect
,但在第一次渲染时不会触发

import * as React from 'react'

export const useLazyEffect:typeof React.useEffect = (cb, dep) => {
  const initializeRef = React.useRef<boolean>(false)

  React.useEffect((...args) => {
    if (initializeRef.current) {
      cb(...args)
    } else {
      initializeRef.current = true
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, dep)
}
import*作为来自“React”的React
导出常量useLazyEffect:typeof React.useffect=(cb,dep)=>{
const initializeRef=React.useRef(false)
React.useffect((…args)=>{
如果(初始化为当前值){
cb(…args)
}否则{
initializeRef.current=true
}
//eslint禁用下一行react HOOK/deps
},副署长)
}

下面的解决方案与上面的类似,只有一点c
const [isMount, setIsMount] = useState(true);
useEffect(()=>{
        if(isMount){
            setIsMount(false);
            return;
        }
        
        //Do anything here for 2nd render onwards
}, [args])
const parentValueRef = useRef(parentValue);
if (parentValue === parentValueRef.current) return;
parentValueRef.current = parentValue;