假设我有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还有助于仅从本地跟踪更改,因此可以防止由于服务器更改而引起的不必要的网络调用。
如果有人正在寻找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;
};
您可以编写一个自定义钩子,使用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])
}
但是,如果您为每个想要单独处理的变更id分别使用两个useEffect,那么阅读和理解起来会更清晰,可能会更好
由于状态与功能组件中的组件实例不是紧密耦合的,因此如果不先保存之前的状态,例如使用useRef,则无法在useEffect中到达之前的状态。这也意味着状态更新可能在错误的位置被错误地实现,因为之前的状态在setState更新函数中可用。
这是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
});
...
选项1 -当值改变时运行useEffect
const Component = (props) => {
useEffect(() => {
console.log("val1 has changed");
}, [val1]);
return <div>...</div>;
};
Demo
选项2 - useHasChanged钩子
比较当前值和以前值是一种常见的模式,它证明了它自己的自定义钩子隐藏了实现细节。
const Component = (props) => {
const hasVal1Changed = useHasChanged(val1)
useEffect(() => {
if (hasVal1Changed ) {
console.log("val1 has changed");
}
});
return <div>...</div>;
};
const useHasChanged= (val: any) => {
const prevVal = usePrevious(val)
return prevVal !== val
}
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
Demo
如果有人正在寻找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;
};