等效于使用 React 钩子的 componentDidUpdate

IT技术 javascript reactjs react-hooks
2021-03-23 16:20:06

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

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

state包含在该定时器启动时的基准,而剩余的时间。effect套间隔称为每秒钟更新的剩余时间,并检查是否回调应该叫。

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

我还尝试通过传递我关心的数组来推动对 props 的更改,但状态保持不变,因此间隔没有重置。

观察数组中的浅层变化以强制仅使用新的钩子 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;

尝试使用key

<RefresherTimer delay={20000} callback={props.updateListOfObjects} key={listOfObjects} />

尝试与props更改一起使用:

<RefresherTimer delay={20000} callback={props.updateListOfObjects} somethingThatChanges={listOfObjects} />

listOfObjects指的是一个对象数组,其中对象本身不一定会改变,因此应该将数组与!==. 通常,该值将来自Redux,该操作updateListOfObjects会导致数组像这样重新初始化:newListOfObjects = [...listOfObjects]

6个回答

所述useRef创建了功能成分的“实例变量”。它作为一个标志来指示它是处于挂载还是更新阶段,而不是更新状态。

const mounted = useRef();
useEffect(() => {
  if (!mounted.current) {
    // do componentDidMount logic
    mounted.current = true;
  } else {
    // do componentDidUpdate logic
  }
});
聪明的主意!Sad React 没有涵盖这种开箱即用的基本场景。
2021-05-25 16:20:06
虽然这不能解决我关于如何基于 props(或复杂键)使组件无效的冗长问题,但它确实回答了标题中的问题,所以这是公认的答案!谢谢!
2021-06-03 16:20:06
多么巧妙的解决方案,谢谢。
2021-06-15 16:20:06
嗨,react-hooks改变了吗?这对我不起作用:stackoverflow.com/questions/65040152/...
2021-06-17 16:20:06

简而言之,当数组的引用发生变化时,您想重置计时器,对吗?如果是这样,您将需要使用一些差异机制,基于纯钩子的解决方案将利用 的第二个参数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
  })
}

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

很好地捕捉到 useEffect() 的第二个参数,它监视属性的变化,然后才触发
2021-06-02 16:20:06
我同意这更符合我的问题。然而,我的问题的标题是什么,我不得不承认摩根是正确的答案。
2021-06-20 16:20:06

使用自定义钩子

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

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

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

@DevinGRhode,签名是相同的。唯一的区别是它不会在初始渲染后运行。谢谢
2021-05-26 16:20:06
看起来它确实像 useEffect 一样工作。谢谢你写这个!
2021-05-29 16:20:06
例子会有所帮助。我试图像这样实现它:useComponentDidUpdate(() => { forecast && scroll(htmlID.forecast); }, [anything]);不管是什么,它都没有按预期工作。空桌子也没有。它似乎只适用于 null 作为依赖项(然后相当于上面投票最多的答案)
2021-06-08 16:20:06
@Kiszuriwalilibori 想分享一个沙盘吗?
2021-06-10 16:20:06
你介意添加一个如何使用它的例子吗?签名是否与 useEffect 本身相同?
2021-06-16 16:20:06

首先创建钩子

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

现在在主函数中

import React, {useEffect, useState} from 'react';
import {Text, View} from 'react-native';
import usePrevious from './usePrevious';

export default function Example() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  

  useEffect(() => {
    // this one is your didupdate method for count variable
    if (count != prevCount) {
      alert('count updated')
    }
  }, [count]);



  return (
    <View>
      <Text>
        You clicked {count} times {prevCount}{' '}
      </Text>
      
      <Text onPress={() => setCount(count + 1)}>Increment</Text>

      <Text onPress={() => setCount(count - 1)}>Decrement</Text>
    </View>
  );
}

您可以使用useUpdateEffectreact-use.