等效于使用 React 钩子的 componentDidUpdate [英] Equivalent to componentDidUpdate using React hooks

查看:16
本文介绍了等效于使用 React 钩子的 componentDidUpdate的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

state 包含对计时器启动时间和剩余时间的引用.effect 设置一个间隔,每秒调用一次以更新剩余时间,并检查是否应该调用回调.

该组件不是要重新安排计时器,也不是在达到零时保持间隔,它应该执行回调和空闲.为了让计时器刷新,我希望将一个数组传递给 key,这将导致组件的状态被重置,从而计时器将重新启动.不幸的是,key 必须与字符串一起使用,因此无论我的数组引用是否发生变化都不会产生任何影响.

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

观察数组中的浅层变化以强制仅使用新的钩子 API 更新状态的首选方法是什么?

import React, { useState, useEffect } from 'react';从 'prop-types' 导入 PropTypes;函数 getTimeRemaining(startedAt, delay) {const now = new Date();const end = new Date(startedAt.getTime() + delay);return Math.max(0, end.getTime() - now.getTime());}函数 RefresherTimer(props) {const [startedAt, setStartedAt] = useState(new Date());const [timeRemaining, setTimeRemaining] = useState(getTimeRemaining(startedAt, props.delay));useEffect(() => {如果(剩余时间 <= 0){//组件设置为空闲,我们不设置间隔.返回;}//设置间隔以每秒刷新组件.const i = setInterval(() => {const nowRemaining = getTimeRemaining(startedAt, props.delay);setTimeRemaining(nowRemaining);if (nowRemaining <= 0) {props.callback();clearInterval(i);}}, 1000);返回 () =>{clearInterval(i);};});let message = `在 ${Math.ceil(timeRemaining/1000)} 秒内刷新.`;如果(剩余时间 <= 0){message = '正在刷新...';}返回<div>{message}</div>;}RefresherTimer.propTypes = {回调:PropTypes.func.isRequired,延迟:PropTypes.number};RefresherTimer.defaultProps = {延迟:2000};导出默认 RefresherTimer;

尝试与 key 一起使用:

尝试与道具更改一起使用:

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

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

解决方案

useRef 创建一个实例变量"在功能组件中.它作为一个标志,表明它是处于挂载阶段还是更新阶段,没有更新状态.

const mount = useRef();useEffect(() => {如果(!mounted.current){//执行 componentDidMount 逻辑mount.current = true;} 别的 {//做 componentDidUpdate 逻辑}});

tldr; How do I simulate componentDidUpdate or otherwise use the key prop with an array to force my component to be reset?

I'm implementing a component which displays a timer and executes a callback when it reaches zero. The intent is for the callback to update a list of objects. The latter component is made of the new React hooks useState and useEffect.

The state contains a reference to the time at which the timer was started, and the time remaining. The effect sets an interval called every second to update the time remaining, and to check whether the callback should be called.

The component is not meant to reschedule a timer, or keep the interval going when it reaches zero, it's supposed to execute the callback and idle. In order for the timer to refresh, I was hoping to pass an array to key which would cause the component's state to be reset, and thus the timer would restart. Unfortunately key must be used with a string, and therefore whether or not my array's reference has changed produces no effect.

I also tried to push changes to the props by passing the array that I was concerned about, but the state was maintained and thus the interval was not reset.

What would be the preferred method to observe shallow changes in an array in order to force a state to be updated solely using the new hooks 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;

Attempted to use with key:

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

Attempted to use with a props change:

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

listOfObjects refers to an array of objects, where the objects themselves won't necessarily change, so the array should be compared with !==. Typically, the value will be coming from Redux, where the action updateListOfObjects causes the array to be reinitialised like so: newListOfObjects = [...listOfObjects].

解决方案

The useRef creates an "instance variable" in functional component. It acts as a flag to indicate whether it is in mount or update phase without updating state.

const mounted = useRef();
useEffect(() => {
  if (!mounted.current) {
    // do componentDidMount logic
    mounted.current = true;
  } else {
    // do componentDidUpdate logic
  }
});

这篇关于等效于使用 React 钩子的 componentDidUpdate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆