使用React钩子等同于componentDidUpdate

FMC*_*orz 27 javascript reactjs react-hooks

tldr; 如何模拟componentDidUpdate或以其他方式使用key数组来强制重置组件?

我正在实现一个显示计时器的组件,并在达到零时执行回调.目的是让回调更新对象列表.后一个组件由新的React钩子 useStateuseEffect.

state包含对计时器启动时间和剩余时间的引用.该effect套间隔称为每秒钟更新的剩余时间,并检查是否回调应该叫.

该组件并不意味着重新安排定时器,或者当它达到零时保持间隔,它应该执行回调和空闲.为了让计时器刷新,我希望传递一个数组,key这将导致组件的状态被重置,因此计时器将重新启动.不幸的是key必须与字符串一起使用,因此我的数组的引用是否已更改不会产生任何影响.

我还试图通过传递我关注的数组来推动对道具的更改,但状态得到维护,因此间隔未重置.

为了强制仅使用新的钩子API更新状态,在数组中观察浅层更改的首选方法是什么?

import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';

function getTimeRemaining(startedAt, delay) {
    const now = new Date();
    const end = new Date(startedAt.getTime() + delay);
    return Math.max(0, end.getTime() - now.getTime());
}

function RefresherTimer(props) {
    const [startedAt, setStartedAt] = useState(new Date());
    const [timeRemaining, setTimeRemaining] = useState(getTimeRemaining(startedAt, props.delay));

    useEffect(() => {

        if (timeRemaining <= 0) {
            // The component is set to idle, we do not set the interval.
            return;
        }

        // Set the interval to refresh the component every second.
        const i = setInterval(() => {
            const nowRemaining = getTimeRemaining(startedAt, props.delay);
            setTimeRemaining(nowRemaining);

            if (nowRemaining <= 0) {
                props.callback();
                clearInterval(i);
            }
        }, 1000);

        return () => {
            clearInterval(i);
        };
    });

    let message = `Refreshing in ${Math.ceil(timeRemaining / 1000)}s.`;
    if (timeRemaining <= 0) {
        message = 'Refreshing now...';
    }

    return <div>{message}</div>;
}

RefresherTimer.propTypes = {
    callback: PropTypes.func.isRequired,
    delay: PropTypes.number
};

RefresherTimer.defaultProps = {
    delay: 2000
};

export default RefresherTimer;
Run Code Online (Sandbox Code Playgroud)

试图使用key:

<RefresherTimer delay={20000} callback={props.updateListOfObjects} key={listOfObjects} />
Run Code Online (Sandbox Code Playgroud)

尝试使用道具更改:

<RefresherTimer delay={20000} callback={props.updateListOfObjects} somethingThatChanges={listOfObjects} />
Run Code Online (Sandbox Code Playgroud)

listOfObjects指对象数组,其中对象本身不一定会改变,因此应该与数组进行比较!==.通常,值将来自Redux,其中操作updateListOfObjects导致数组重新初始化,如下所示:newListOfObjects = [...listOfObjects].

Mor*_*eng 33

所述useRef创建了功能成分的“实例变量”。它充当标志以指示它是处于安装阶段还是处于更新阶段而不处于更新状态。

const mounted = useRef();
useEffect(() => {
  if (!mounted.current) {
    mounted.current = true;
  } else {
    // do componentDidUpate logic
  }
});
Run Code Online (Sandbox Code Playgroud)

  • 聪明的主意!Sad React没有开箱即用地涵盖这种基本场景。 (7认同)

Bea*_*oot 9

简而言之,你想在数组的引用改变时重置你的计时器,对吗?如果是这样,您将需要使用一些差异机制,基于纯钩子的解决方案将利用 的第二个参数useEffect,如下所示:

function RefresherTimer(props) {
  const [startedAt, setStartedAt] = useState(new Date());
  const [timeRemaining, setTimeRemaining] = useState(getTimeRemaining(startedAt, props.delay));

  //reset part, lets just set startedAt to now
  useEffect(() => setStartedAt(new Date()),
    //important part
    [props.listOfObjects] // <= means: run this effect only if any variable
    // in that array is different from the last run
  )

  useEffect(() => {
    // everything with intervals, and the render
  })
}
Run Code Online (Sandbox Code Playgroud)

有关此行为的更多信息https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects


Ben*_*arp 5

使用自定义钩子

export const useComponentDidUpdate = (effect, dependencies) => {
  const hasMounted = useRef(false);

  useEffect(
    () => {
      if (!hasMounted.current) {
        hasMounted.current = true;
        return;
      }
      effect();
    }, 
    dependencies
  );
};

Run Code Online (Sandbox Code Playgroud)

效果将不会在初始渲染后运行。此后,它取决于应观察的值数组。如果它是空的,它将在每次渲染后运行。否则,它会在其中一个值发生更改时运行。