React钩子useEffect仅在更新时有效吗?

时间:2019-03-09 08:53:22

标签: reactjs react-hooks

如果我们想限制useEffect仅在组件安装时才运行,则可以在useEffect上添加[]的第二个参数。

useEffect(() => {
  // ...
}, []);

但是如何使useEffect仅在组件被更新的那一刻(初始安装除外)才能运行?

8 个答案:

答案 0 :(得分:13)

如果您要运行useEffect仅在除初始安装以外的更新上运行,则可以使用useRef来跟踪带有useEffect的initialMount,而无需第二个参数。

const isInitialMount = useRef(true);

useEffect(() => {
  if (isInitialMount.current) {
     isInitialMount.current = false;
  } else {
      // Your useEffect code here to be run on update
  }
});

答案 1 :(得分:6)

我真的很喜欢Shubham的回复,所以我把它做成了自定义的挂钩

/**
 * A custom useEffect hook that only triggers on updates, not on initial mount
 * Idea stolen from: https://stackoverflow.com/a/55075818/1526448
 * @param {Function} effect
 * @param {Array<any>} dependencies
 */
export default function useUpdateEffect(effect, dependencies = []) {
  const isInitialMount = useRef(true);

  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
    } else {
      effect();
    }
  }, dependencies);
}

答案 2 :(得分:2)

您可以通过将状态设置为非布尔初始值(例如空值)来解决此问题:

  const [isCartOpen,setCartOpen] = useState(null);
  const [checkout,setCheckout] = useState({});

  useEffect(() => {

    // check to see if its the initial state
    if( isCartOpen === null ){

      // first load, set cart to real initial state, after load
      setCartOpen( false );
    }else if(isCartOpen === false){

      // normal on update callback logic
      setCartOpen( true );
    }
  }, [checkout]);

答案 3 :(得分:1)

Shubham和Mario都提出了正确的方法,但是代码仍然不完整,没有考虑以下情况。

  1. 如果卸载组件,则应重置其标志
  2. 传递的effect函数可能会从中返回一个清理函数,该函数永远不会被调用

将更完整的代码分享到下面,该代码涵盖了上面两个遗漏的情况:

import React from 'react';

const useIsMounted = function useIsMounted() {
  const isMounted = React.useRef(false);

  React.useEffect(function setIsMounted() {
    isMounted.current = true;

    return function cleanupSetIsMounted() {
      isMounted.current = false;
    };
  }, []);

  return isMounted;
};

const useUpdateEffect = function useUpdateEffect(effect, dependencies) {
  const isMounted = useIsMounted();
  const isInitialMount = React.useRef(true);

  React.useEffect(() => {
    let effectCleanupFunc = function noop() {};

    if (isInitialMount.current) {
      isInitialMount.current = false;
    } else {
      effectCleanupFunc = effect() || effectCleanupFunc;
    }
    return () => {
      effectCleanupFunc();
      if (!isMounted.current) {
        isInitialMount.current = true;
      }
    };
  }, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
};

答案 4 :(得分:0)

从Subham的答案中寻求帮助此代码仅在特定项目更新上运行,而不是在每次更新时都运行,而不是在组件初始安装时运行。

const isInitialMount = useRef(true);    //useEffect to run only on updates except initial mount


//useEffect to run only on updates except initial mount
  useEffect(() => {
    if (isInitialMount.current) {
        isInitialMount.current = false;
     } else {              
         if(fromScreen!='ht1' && appStatus && timeStamp){
            // let timeSpentBG = moment().diff(timeStamp, "seconds");
            // let newHeatingTimer = ((bottomTab1Timer > timeSpentBG) ? (bottomTab1Timer - timeSpentBG) : 0);
            // dispatch({
            //     type: types.FT_BOTTOM_TAB_1,
            //     payload: newHeatingTimer,
            // })
            // console.log('Appstaatus', appStatus, timeSpentBG, newHeatingTimer)
         }
     }
  }, [appStatus])

答案 5 :(得分:0)

较短的

const [mounted, setMounted] = useRef(false)

useEffect(() => {
  if(!mounted) return setMounted(true)
  // your update codes goes here
});

答案 6 :(得分:0)

使用useEffect Cleanup函数,而不使用空数组作为第二个参数:

useEffect(() => { 
  return () => {
  // your code to be run only on update
  }
});

如果愿意,可以使用另一个useEffect(将空数组作为第二个参数)进行初始安装,将代码照常放置在其主要功能中。

答案 7 :(得分:-1)

另一种方法是根据关注的程度两次调用useEffect。 请从博客文章下方的注释中查看RudyNappée的回答:

https://dev.to/savagepixie/how-to-mimic-componentdidupdate-with-react-hooks-3j8c

简而言之:

useEffect(concern1, [...]);
useEffect(concern2, [...]);
...

对于useEffect的每个实例,传递一个您尝试监视的不同数组参数。