React 中 useLatest 的并发安全版本?

Leo*_*ang 6 javascript reactjs react-hooks

有一个常用的实用程序钩子“useLatest”,它返回一个包含输入的最新值的引用。有两种常见的实现:

const useLatest = <T>(value: T): { readonly current: T } => {
  const ref = useRef(value);
  ref.current = value;
  return ref;
};
Run Code Online (Sandbox Code Playgroud)

来自https://github.com/streamich/react-use/blob/master/src/useLatest.ts

const useLatest = <T extends any>(current: T) => {
  const storedValue = React.useRef(current)
  React.useEffect(() => {
    storedValue.current = current
  })
  return storedValue
}
Run Code Online (Sandbox Code Playgroud)

来自https://github.com/jaredLunde/react-hook/blob/master/packages/latest/src/index.tsx

第一个版本不适合 React 18 的并发模式,如果在useEffect运行之前(例如在渲染期间)使用,第二个版本将返回旧值。

有没有办法实现既安全又一致返回正确值的方法?

这是我的尝试:

function useLatest<T>(val: T): React.MutableRefObject<T> {
  const ref = useRef({
    tempVal: val,
    committedVal: val,
    updateCount: 0,
  });
  ref.current.tempVal = val;
  const startingUpdateCount = ref.current.updateCount;

  useLayoutEffect(() => {
    ref.current.committedVal = ref.current.tempVal;
    ref.current.updateCount++;
  });

  return {
    get current() {
      // tempVal is from new render, committedVal is from old render.
      return ref.current.updateCount === startingUpdateCount
        ? ref.current.tempVal
        : ref.current.committedVal;
    },
    set current(newVal: T) {
      ref.current.tempVal = newVal;
    },
  };
}
Run Code Online (Sandbox Code Playgroud)

这还没有经过彻底的测试,只是在写这个问题的时候写的,但它似乎大部分时间都有效。它应该比上面的两个版本都好,但它有两个问题:它每次都返回一个不同的对象,并且在这种情况下仍然可能不一致:

渲染 1:

  1. ref1 = useLatest(val1)
  2. 创建 function1,它引用 ref1
  3. 提交(useLayoutEffect 运行)

渲染 2:

  1. 使用最新(val2)
  2. 调用函数1

function1 将使用 val1,但它应该使用 val2。

Izh*_*aki 3

以下是我认为正确的:

const useLatest = <T extends any>(current: T) => {
  const storedValue = React.useRef(current)
  React.useLayoutEffect(() => {
    storedValue.current = current
  })
  return storedValue.current
}
Run Code Online (Sandbox Code Playgroud)

  • 好的。那是因为我们在这里使用引用,并且它们不会重新渲染任何内容。我想问题是:实际的用例是什么,例如,我们试图存储最新的是什么?您可以创建一个带有示例的codesandbox吗? (3认同)