如何在React Hooks useEffect上比较oldValues和newValues?

假设我有3个输入:rate,sendAmount和receiveAmount。我把那3个输入放在useEffect diffing params上。规则是:

  • 如果sendAmount改变了,我计算 receiveAmount = sendAmount * rate
  • 如果receiveAmount改变了,我计算 sendAmount = receiveAmount / rate
  • 如果费率发生变化,我计算receiveAmount = sendAmount * rate时间sendAmount > 0或计算sendAmount = receiveAmount / rate时间receiveAmount > 0

这是codesandbox https://codesandbox.io/s/pkl6vn7x6j来演示此问题。

有没有一种方法可以比较oldValuesnewValues喜欢,componentDidUpdate而不是为此情况制作3个处理程序?

谢谢


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

在这种情况下,我不能使用多个,useEffect因为每次更改都会导致相同的网络呼叫。这就是为什么我也使用changeCount跟踪更改的原因changeCount也有助于跟踪仅来自本地的更改,因此我可以防止由于服务器的更改而导致不必要的网络呼叫。

Eva梅2020/03/12 12:44:15

由于状态不会与功能组件中的组件实例紧密耦合,因此,如果不先useEffect保存状态,就无法进入先前的状态useRef这也意味着状态更新可能在错误的位置错误地实现了,因为以前的状态在setStateupdater函数中可用

这是一个很好的用例useReducer,提供了类似Redux的存储,并允许实现各自的模式。状态更新是显式执行的,因此无需弄清楚哪个状态属性已更新。从派遣行动中已经很清楚了。

这是一个示例,看起来可能像这样:

function reducer({ sendAmount, receiveAmount, rate }, action) {
  switch (action.type) {
    case "sendAmount":
      sendAmount = action.payload;
      return {
        sendAmount,
        receiveAmount: sendAmount * rate,
        rate
      };
    case "receiveAmount":
      receiveAmount = action.payload;
      return {
        sendAmount: receiveAmount / rate,
        receiveAmount,
        rate
      };
    case "rate":
      rate = action.payload;
      return {
        sendAmount: receiveAmount ? receiveAmount / rate : sendAmount,
        receiveAmount: sendAmount ? sendAmount * rate : receiveAmount,
        rate
      };
    default:
      throw new Error();
  }
}

function handleChange(e) {
  const { name, value } = e.target;
  dispatch({
    type: name,
    payload: value
  });
}

...
const [state, dispatch] = useReducer(reducer, {
  rate: 2,
  sendAmount: 0,
  receiveAmount: 0
});
...
Tony村村2020/03/12 12:44:15

我刚刚发布了react-delta,它解决了这种情况。我认为useEffect责任太多。

职责范围

  1. 它使用以下命令比较其依赖项数组中的所有值 Object.is
  2. 它根据#1的结果运行效果/清理回调

分手责任

react-deltauseEffect责任分解为几个较小的钩子。

责任一

责任二

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

逆天西里2020/03/12 12:44:15

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

让我们用usePrevious有人评论的情况来看看这种情况

  1. prop.minTime:5 ==>参考电流= 5 | 设定参考电流
  2. prop.minTime:5 ==>参考电流= 5 | 新值等于参考电流
  3. prop.minTime:8 ==>参考电流= 5 | 新值不等于参考电流
  4. prop.minTime:5 ==>参考电流= 5 | 新值等于参考电流

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

L理查德2020/03/12 12:44:15

对于真正简单的道具比较,您可以使用useEffect和useState轻松检查道具是否已更新。

const myComponent = ({ prop }) => {
  const [propHasChanged, setPropHasChanged] = useState(false)
  useEffect(() => {
    setPropHasChanged(true)
  }, [prop])

  if(propHasChanged){
    ~ do stuff here ~
  }
}

useEffect检查该道具是否已更改,更新状态说它已经拥有,并允许您的条件代码运行。

达蒙西里2020/03/12 12:44:15

您可以编写一个自定义钩子,以使用useRef为您提供以前的道具

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

然后用在 useEffect

const Component = (props) => {
    const {receiveAmount, sendAmount } = props
    const prevAmount = usePrevious({receiveAmount, sendAmount});
    useEffect(() => {
        if(prevAmount.receiveAmount !== receiveAmount) {

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

         // process here
        }
    }, [receiveAmount, sendAmount])
}

但是,如果您useEffect对每个更改ID分别使用两个,则它更清晰,并且可能更好,更清晰地阅读和理解,您想分别处理它们

乐猪猪2020/03/12 12:44:15

选项1-useCompare挂钩

将当前值与以前的值进行比较是一种常见的模式,它证明了自己的自定义钩子是正确的,从而隐藏了实现细节。

const useCompare = (val: any) => {
    const prevVal = usePrevious(val)
    return prevVal !== val
}

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

const Component = (props) => {
  ...
  const hasVal1Changed = useCompare(val1)
  const hasVal2Changed = useCompare(val2);
  useEffect(() => {
    if (hasVal1Changed ) {
      console.log("val1 has changed");
    }
    if (hasVal2Changed ) {
      console.log("val2 has changed");
    }
  });

  return <div>...</div>;
};

演示版

选项2-值更改时运行useEffect

const Component = (props) => {
  ...
  useEffect(() => {
    console.log("val1 has changed");
  }, [val1]);
  useEffect(() => {
    console.log("val2 has changed");
  }, [val2]);

  return <div>...</div>;
};

演示版

猿路易2020/03/12 12:44:15

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

import { useEffect, useRef } from "react";

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