usePrevious用于保存上一次渲染时的状态。
React官方文档提供了一个实现:
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}
复制代码usePrevious记录的值初始为空,每轮渲染后记录状态值,这样每次渲染返回的便是上一轮渲染时的值。
react-use同样使用了此实现。
ahooks则为用户提供了compare,可以让用户决定是否更新usePrevious记录的值。
import { useRef } from 'react';
export type compareFunction<T> = (prev: T | undefined, next: T) => boolean;
function usePrevious<T>(state: T, compare?: compareFunction<T>): T | undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>();
  const needUpdate = typeof compare === 'function' ? compare(curRef.current, state) : true;
  if (needUpdate) {
    prevRef.current = curRef.current;
    curRef.current = state;
  }
  return prevRef.current;
}
export default usePrevious;
复制代码ahooks 使用了两个ref,一个记录当前值,一个记录之前的值。不过为什么要这样实现呢?这样实现与react-use的实现方式有什么不同??
在一番试验无果后,随意搜索了下却找到了这个issue
什么?ahooks的实现不符合使用规范?
在issue作者给出的链接中说明了在render时读取或修改ref的值时会进行警告,并且Dan在回复中说明了这样做的原因。
大意是在render时读取ref的值和读取一个随机的全局变量一样。读取的值是什么取决于何时调用render。如果React调用在稍微不同的时间渲染,可能会得到不同的结果。
在未来React默认开启Concurrent模式后,ahooks的实现便会出现问题。
issue作者除了给出解释之外,还提供了一个demo。demo中使用的usePrevious在StrictMode下有了不同的行为。
不过demo中渲染用的也是legacy模式,那为什么在StrictMode下行为会不同??
打断点调试了一番,发现进入页面时usePrevious居然被调用了两次,导致curRef和preRef记录的状态出现了问题。
在React issue中搜索StrictMode、twice等关键字找到了原因,还是我们的Dan神回复的:
使用了StrictMode且用了Hooks的组件会在开发模式时渲染两次。StrictMode的一个主要目的是方便将现有项目迁移到未来使用concurrent模式的React版本中,会这么设计不奇怪。
至此,作战告捷?
简单改了下ahooks的usePrevious实现。
function usePrevious<T>(state: T, compare?: (prev: T | undefined, next: T) => boolean): T | undefined {
  const ref = useRef<T>();
  useEffect(() => {
    const needUpdate = typeof compare === 'function' ? compare(ref.current, state) : true;
    if (needUpdate) {
      ref.current = state;
    }
  });
  return ref.current;
}
复制代码





















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
