提问者:小点点

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


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

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

但上面的示例在初始渲染时应用效果,并在count已更改的后续重新渲染时应用效果。如何告诉React跳过初始渲染上的效果?


共3个答案

匿名用户

正如《指南》所述,

效果挂钩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)
      return fn();
    else
      didMountRef.current = true;
  }, inputs);
}

建议useRef而不是setState的学分转到@Tholle。

匿名用户

这里有一个自定义钩子,它只提供了一个布尔标志来指示当前渲染是否是第一次渲染(当组件被挂载时)。它与其他一些答案大致相同,但您可以在use效应或渲染函数或组件中的任何其他地方使用标志。也许有人可以提出一个更好的名字。

import { useRef, useEffect } from 'react';

export const useIsMount = () => {
  const isMountRef = useRef(true);
  useEffect(() => {
    isMountRef.current = false;
  }, []);
  return isMountRef.current;
};

您可以像这样使用它:

import React, { useEffect } from 'react';

import { useIsMount } from './useIsMount';

const MyComponent = () => {
  const isMount = useIsMount();

  useEffect(() => {
    if (isMount) {
      console.log('First Render');
    } else {
      console.log('Subsequent Render');
    }
  });

  return isMount ? <p>First Render</p> : <p>Subsequent Render</p>;
};

如果你感兴趣,这里有一个测试:

import { renderHook } from '@testing-library/react-hooks';

import { useIsMount } from '../useIsMount';

describe('useIsMount', () => {
  it('should be true on first render and false after', () => {
    const { result, rerender } = renderHook(() => useIsMount());
    expect(result.current).toEqual(true);
    rerender();
    expect(result.current).toEqual(false);
    rerender();
    expect(result.current).toEqual(false);
  });
});

我们的用例是隐藏动画元素,如果初始道具表明它们应该被隐藏。在后来的渲染中,如果道具改变了,我们确实希望元素动画出来。

匿名用户

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

useEffect(() => {
  // skip initial render
  return () => {
    // do something with dependency
  }
}, [dependency])

这只是一个例子,如果您的案例非常简单,那么还有其他方法可以做到这一点。

这样做的缺点是您不能有清理效果,并且只有在依赖数组第二次更改时才会执行。

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

更清楚地说,你不应该使用这种方法来解决问题中的问题(跳过初始渲染),这只是为了教学目的,表明你可以用不同的方式做同样的事情。如果需要跳过初始渲染,请使用其他答案的方法。