首页 > 解决方案 > usePreviousDistinct 有 useEffect 和没有 useEffect 之间的区别

问题描述

我需要一个钩子来获取特定状态的先前不同值。它看起来像这样,它似乎工作:

function usePreviousDistinct(state) {
  const prevRef = useRef();

  useEffect(() => {
    prevRef.current = state;
  }, [state]);

  return prevRef.current;
}

我还看到react-use 包usePreviousDistinct中有一个钩子,但方法与我的不同。

import { useRef } from 'react';
import { useFirstMountState } from './useFirstMountState';

export type Predicate<T> = (prev: T | undefined, next: T) => boolean;

const strictEquals = <T>(prev: T | undefined, next: T) => prev === next;

export default function usePreviousDistinct<T>(value: T, compare: Predicate<T> = strictEquals): T | undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>(value);
  const isFirstMount = useFirstMountState();

  if (!isFirstMount && !compare(curRef.current, value)) {
    prevRef.current = curRef.current;
    curRef.current = value;
  }

  return prevRef.current;
}

我想知道我是否没有理解某些东西或遗漏了什么。我的版本也正确吗?

在我的测试中我找不到区别: https ://codesandbox.io/s/distracted-mayer-zpym8?file=/src/App.js

标签: javascriptreactjsreact-hooks

解决方案


  • useEffect()useRef()(您的版本)一起显示最新值。
  • useRef()withoutuseEffect()为您提供正确的值,因为它同步运行,但没有异步带来的优势。
  • useEffect()withuseState()为您提供正确的值,但可能会触发不必要的渲染(可能会增加不必要的开销)。

您的版本看起来像预期的那样工作,因为显示的旧值是您希望看到的新值。但实际的新值并不是你想要的。

例子:

import React, { useState, useEffect, useRef } from 'react';

export const MyComponent = function(props){
    const [state, setState ] = useState(0);

    return <React.Fragment>
        state: { state },<br />
        with useEffect: { usePreviousDistinctUE( state ) },<br />
        w/o useEffect: { usePreviousDistinctR( state ) },<br />

        <button onClick={ function(){
            setState( state + 1 );
        } }>
            increment
        </button>
    </React.Fragment>;
};

const usePreviousDistinctUE = function( value ){
    const prevRef = useRef();

    useEffect(() => {
        prevRef.current = value;
        console.log('with useEffect, prev:', prevRef.current, ', current:', value);
    }, [value]);

    return prevRef.current;
};

const usePreviousDistinctR = function( value ){
    const prevRef = useRef();
    const curRef = useRef( value );

    if( curRef.current !== value ){
        prevRef.current = curRef.current;
        curRef.current = value;
    }

    console.log('w/o useEffect, prev:', prevRef.current, ', current:', curRef.current);
    return prevRef.current;
};

页面上显示的值是相同的,但在控制台中它们是不同的。这意味着useEffect()版本中的值已更改,只是尚未显示在页面上。

如果您只是添加另一个钩子来更新任何不相关的内容(保持其他所有内容不变),那么页面(可能*)会神奇地再次显示更新的值,因为页面被重新渲染并显示先前已经更改的值。现在你眼中的值是错误的,但它没有改变,只显示

// ...
    with useEffect: { usePreviousDistinctUE( state ) },<br />
    w/o useEffect: { usePreviousDistinctR( state ) },<br />
    anything updated: { useAnythingUpdating( state ) },<br />
// ...

const useAnythingUpdating = function(state){
    const [result, setResult ] = useState(0);
    useEffect(() => {
        setResult( state );
        console.log('anything updated');
    });
    return result;
};

*但你不应该依赖其他触发重新渲染的东西。我什至不确定这会在所有情况下按预期更新。

更多细节:

useEffect()prop在 react 决定必须更改时触发。然后ref更改了,但是 react 不会“得到通知”有关ref更改,因此它认为没有必要重新渲染页面以显示更改的值。

没有useEffect()变化的例子中是同步发生的。React 也不“知道”这种变化,但是(如果其他一切都按预期运行)无论如何都会在必要时重新渲染(您将在最后渲染的另一个函数中调用该函数)。

(不通知 react 变化基本上是使用的重点useRef():有时你只需要一个值,在你自己的控制下,而不用 react 用它做魔法。)


推荐阅读