假设我有3个输入:rate、sendAmount和receiveAmount。我把这3个输入放到useEffect上。规则如下:

如果sendAmount改变了,我计算receiveAmount = sendAmount * rate 如果receiveAmount改变了,我计算sendAmount = receiveAmount / rate 如果速率发生变化,当sendAmount > 0时,我计算receiveAmount = sendAmount * rate;当receiveAmount > 0时,我计算sendAmount = receiveAmount / rate

下面是代码和框https://codesandbox.io/s/pkl6vn7x6j来演示这个问题。

是否有一种方法来比较oldValues和newValues,如componentDidUpdate,而不是为这种情况下做3处理程序?

谢谢


下面是我使用usePrevious的最终解决方案 https://codesandbox.io/s/30n01w2r06

在本例中,我不能使用多个useEffect,因为每次更改都会导致相同的网络调用。这就是为什么我还使用changeCount来跟踪更改。这个changeCount还有助于仅从本地跟踪更改,因此可以防止由于服务器更改而引起的不必要的网络调用。


当前回答

使用Ref会在应用程序中引入一种新的错误。

让我们看看这个例子,使用usePrevious,之前有人评论过:

道具。minTime: 5 ==> ref.current = 5 | set ref.current 道具。minTime: 5 ==> ref.current = 5 |的新值等于ref.current 道具。minTime: 8 ==> ref.current = 5 |的新值不等于ref.current 道具。minTime: 5 ==> ref.current = 5 |的新值等于ref.current

正如我们在这里看到的,我们没有更新内部引用,因为我们使用的是useEffect

其他回答

在你的例子中(简单对象):

useEffect(()=>{
  // your logic
}, [rate, sendAmount, receiveAmount])

在其他情况下(复杂对象)

const {cityInfo} = props;
useEffect(()=>{
  // some logic
}, [cityInfo.cityId])

如果有人正在寻找usePrevious的TypeScript版本:

在.tsx模块中:

import { useEffect, useRef } from "react";

const usePrevious = <T extends unknown>(value: T): T | undefined => {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

或者在.ts模块中:

import { useEffect, useRef } from "react";

const usePrevious = <T>(value: T): T | undefined => {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

小心大多数投票的答案。对于上述更复杂的场景,usePrevious的变化可能会给你太多的重渲染(1)或与original(2)相同的值。

我们必须:

在useEffect中添加[value]作为依赖项,仅当值发生变化时重新运行 将JSON.parse(JSON.stringify(value))(或一些深度拷贝)赋值给useEffect中的ref.current,以防止将state的引用传递给ref而不是值

升级看点:

const usePrevious = <T>(value: T): T => {
  const ref: any = useRef<T>()

  useEffect(() => {
    ref.current = JSON.parse(JSON.stringify(value))
  }, [value])

  return ref.current
}

我刚刚发布了react-delta,它解决了这种情况。在我看来,useEffect有太多的责任。

责任

它使用Object.is比较其依赖数组中的所有值 它基于#1的结果运行effect/cleanup回调

分解责任

react-delta将useEffect的职责分解为几个更小的钩子。

责任# 1

usePrevious(值) useLatest(值) useDelta(价值,选项) useDeltaArray (valueArray选项) useDeltaObject (valueObject选项) 一些(deltaArray) 每一个(deltaArray)

责任# 2

布尔useConditionalEffect(回调)

根据我的经验,这种方法比useEffect/useRef解决方案更灵活、干净和简洁。

离开公认的答案,一个不需要自定义钩子的替代解决方案:

const Component = ({ receiveAmount, sendAmount }) => {
  const prevAmount = useRef({ receiveAmount, sendAmount }).current;

  useEffect(() => {
    if (prevAmount.receiveAmount !== receiveAmount) {
     // process here
    }

    if (prevAmount.sendAmount !== sendAmount) {
     // process here
    }

    return () => { 
      prevAmount.receiveAmount = receiveAmount;
      prevAmount.sendAmount = sendAmount;
    };
  }, [receiveAmount, sendAmount]);
};

这假设您实际上需要引用前面“process here”位中的任何值。否则,除非你的条件超出了一个直接的!==比较,这里最简单的解决方案是:

const Component = ({ receiveAmount, sendAmount }) => {
  useEffect(() => {
     // process here
  }, [receiveAmount]);

  useEffect(() => {
     // process here
  }, [sendAmount]);
};