根据文件:

componentDidUpdate()在更新发生后立即调用。初始呈现时不调用此方法。

我们可以使用新的useEffect()钩子来模拟componentDidUpdate(),但似乎每次渲染后都会运行useEffect(),甚至是第一次渲染。我如何让它在初始渲染时不运行?

正如你在下面的例子中看到的,componentDidUpdateFunction在初始渲染期间被打印,但是componentDidUpdateClass在初始渲染期间没有被打印。

function ComponentDidUpdateFunction() { const [count, setCount] = React.useState(0); React.useEffect(() => { console.log("componentDidUpdateFunction"); }); return ( <div> <p>componentDidUpdateFunction: {count} times</p> <button onClick={() => { setCount(count + 1); }} > Click Me </button> </div> ); } class ComponentDidUpdateClass extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } componentDidUpdate() { console.log("componentDidUpdateClass"); } render() { return ( <div> <p>componentDidUpdateClass: {this.state.count} times</p> <button onClick={() => { this.setState({ count: this.state.count + 1 }); }} > Click Me </button> </div> ); } } ReactDOM.render( <div> <ComponentDidUpdateFunction /> <ComponentDidUpdateClass /> </div>, document.querySelector("#app") ); <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div>


当前回答

简化的实现

import { useRef, useEffect } from 'react';

function MyComp(props) {

  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    } else {
      myProp = 'some val';
    };

  }, [props.myProp])


  return (
    <div>
      ...
    </div>
  )

}

其他回答

@ravi,你的没有调用传入的卸载函数。下面是一个更完整的版本:

/** * Identical to React.useEffect, except that it never runs on mount. This is * the equivalent of the componentDidUpdate lifecycle function. * * @param {function:function} effect - A useEffect effect. * @param {array} [dependencies] - useEffect dependency list. */ export const useEffectExceptOnMount = (effect, dependencies) => { const mounted = React.useRef(false); React.useEffect(() => { if (mounted.current) { const unmount = effect(); return () => unmount && unmount(); } else { mounted.current = true; } }, dependencies); // Reset on unmount for the next mount. React.useEffect(() => { return () => mounted.current = false; }, []); };

我认为创建一个自定义钩子将是多余的,我不想通过使用useLayoutEffect钩子的一些与布局无关的东西来混淆我的组件的可读性,所以,在我的情况下,我只是检查我的有状态变量selectedItem的值是否触发useEffect回调是它的原始值,以确定它是否是初始渲染:

export default function MyComponent(props) {
    const [selectedItem, setSelectedItem] = useState(null);

    useEffect(() => {
        if(!selectedItem) return; // If selected item is its initial value (null), don't continue
        
        //... This will not happen on initial render

    }, [selectedItem]);

    // ...

}

这是迄今为止我使用typescript创建的最好的实现。基本上,想法是相同的,使用Ref,但我也考虑了useEffect返回的回调,以执行组件卸载时的清理。

import {
  useRef,
  EffectCallback,
  DependencyList,
  useEffect
} from 'react';

/**
 * @param effect 
 * @param dependencies
 *  
 */
export default function useNoInitialEffect(
  effect: EffectCallback,
  dependencies?: DependencyList
) {
  //Preserving the true by default as initial render cycle
  const initialRender = useRef(true);

  useEffect(() => {
    let effectReturns: void | (() => void) = () => {};

    // Updating the ref to false on the first render, causing
    // subsequent render to execute the effect
    if (initialRender.current) {
      initialRender.current = false;
    } else {
      effectReturns = effect();
    }

    // Preserving and allowing the Destructor returned by the effect
    // to execute on component unmount and perform cleanup if
    // required.
    if (effectReturns && typeof effectReturns === 'function') {
      return effectReturns;
    } 
    return undefined;
  }, dependencies);
}

你可以简单地使用它,就像你通常使用useEffect钩子一样,但这一次,它不会在初始渲染时运行。下面是如何使用这个钩子。

useNoInitialEffect(() => {
  // perform something, returning callback is supported
}, [a, b]);

如果你使用ESLint并且想为这个自定义钩子使用react-hooks/竭-deps规则:

{
  "rules": {
    // ...
    "react-hooks/exhaustive-deps": ["warn", {
      "additionalHooks": "useNoInitialEffect"
    }]
  }
}

简化的实现

import { useRef, useEffect } from 'react';

function MyComp(props) {

  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    } else {
      myProp = 'some val';
    };

  }, [props.myProp])


  return (
    <div>
      ...
    </div>
  )

}

    const [dojob, setDojob] = useState(false);

    yourfunction(){
        setDojob(true);
    }

    useEffect(()=>{
        if(dojob){
            yourfunction();
            setDojob(false);
        }
    },[dojob]);