假设我有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还有助于仅从本地跟踪更改,因此可以防止由于服务器更改而引起的不必要的网络调用。
下面是Aadit M Shah的答案的Typescript版本。
我把它从useTransition重命名为usePrevious,因为useTransition已经存在于React中。
import { useEffect, useRef, useState } from 'react';
const usePrevious = <T extends any[],>(callback: (prev: T) => void, deps: T): void => {
const callbackRef = useRef<null | ((prev: T) => void)>(null);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const depsRef = useRef<null | T>(null);
const [initial, setInitial] = useState(true);
useEffect(() => {
if (!initial && depsRef.current !== null && callbackRef.current !== null) {
callbackRef.current(depsRef.current);
}
depsRef.current = deps;
setInitial(false);
}, deps);
}
export default usePrevious;
用法:
usePrevious<[boolean]>(([prevIsOpen]) => {
console.log('prev', prevIsOpen);
console.log('now', isOpen);
}, [isOpen])
离开公认的答案,一个不需要自定义钩子的替代解决方案:
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]);
};
如果有人正在寻找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更直观。
import { useRef, useEffect } from 'react'
// useTransition :: Array a => (a -> Void, a) -> Void
// |_______| |
// | |
// callback deps
//
// The useTransition hook is similar to the useEffect hook. It requires
// a callback function and an array of dependencies. Unlike the useEffect
// hook, the callback function is only called when the dependencies change.
// Hence, it's not called when the component mounts because there is no change
// in the dependencies. The callback function is supplied the previous array of
// dependencies which it can use to perform transition-based effects.
const useTransition = (callback, deps) => {
const func = useRef(null)
useEffect(() => {
func.current = callback
}, [callback])
const args = useRef(null)
useEffect(() => {
if (args.current !== null) func.current(...args.current)
args.current = deps
}, deps)
}
您可以像下面那样使用useTransition。
useTransition((prevRate, prevSendAmount, prevReceiveAmount) => {
if (sendAmount !== prevSendAmount || rate !== prevRate && sendAmount > 0) {
const newReceiveAmount = sendAmount * rate
// do something
} else {
const newSendAmount = receiveAmount / rate
// do something
}
}, [rate, sendAmount, receiveAmount])
希望这能有所帮助。
下面是Aadit M Shah的答案的Typescript版本。
我把它从useTransition重命名为usePrevious,因为useTransition已经存在于React中。
import { useEffect, useRef, useState } from 'react';
const usePrevious = <T extends any[],>(callback: (prev: T) => void, deps: T): void => {
const callbackRef = useRef<null | ((prev: T) => void)>(null);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const depsRef = useRef<null | T>(null);
const [initial, setInitial] = useState(true);
useEffect(() => {
if (!initial && depsRef.current !== null && callbackRef.current !== null) {
callbackRef.current(depsRef.current);
}
depsRef.current = deps;
setInitial(false);
}, deps);
}
export default usePrevious;
用法:
usePrevious<[boolean]>(([prevIsOpen]) => {
console.log('prev', prevIsOpen);
console.log('now', isOpen);
}, [isOpen])