useState、UseRef 或 useMemo,我更喜欢哪个

useState, UseRef, or useMemo, what should I prefer

我有一个场景,我需要在整个组件生命周期中存储函数的输出(这个值永远不会改变)。

考虑下面的例子

const UniqueIdView = () => {
    const [uniqueIdState1] = useState(() => uniqueId('prefix_'))
    const [uniqueIdState2] = useState(uniqueId('prefix_'))
    const uniqueIdRef = useRef(uniqueId('prefix_'))
    const uniqueIdMemo = useMemo(() => uniqueId('prefix_'), [])
    return (
        <div>
            {uniqueIdState1}
            {uniqueIdState2}
            {uniqueIdRef.current}
            {uniqueIdMemo}
       </div>
    )
}

上述 4 种方法中哪一种是理想的?

我的理解:

useState 应用于存储值,其中值的更改应触发重新渲染。

useMemo 应该在我想记忆计算的情况下使用,记忆总是有相关的成本。

所以,useRef在我看来是合适的。

但是,我有一个困惑:

useRef 将在每次重新渲染时一次又一次地触发我的函数,而使用 useState 的回调方法只会触发我的函数一次。

但是再一次,如果我不得不考虑一次又一次调用函数的成本,我是否应该使用useMemo(但是在这种情况下,函数并不复杂,我们是否应该增加记忆开销)?

更新

我想达到什么目标?

我想创建一个自定义挂钩,它应该 return uniqueId 在重新渲染时不应该改变

const UniqueId = () {
    const uniqueId = useStableUniqueId('prefix__')
    return <div>{uniqueId}<div>
}

因此无论 UniqueId 重新呈现多少次,该值都不应更改。

传递给useRef的值只是初始值,但如果是函数调用,它实际上会在每次渲染时被调用。不太确定你的问题的其余部分。每个挂钩都有特定的用途。选择满足您需求的那个。

I have a scenario where I need to store the output of a function throughout the component life cycle.

对我来说,明确的选择是 useMemo 钩子来记忆 可能 昂贵的函数调用的结果值。

不是定期更新所以useState不适合。如果您决定将它存储在状态中并且需要更新它,您将需要一个 useEffect 具有依赖性的挂钩并重新计算一个新值并调用状态更新程序函数。这本质上是 useMemo 钩子。

如果您决定将其存储在 React ref 中,那么您将再次需要将其与 useEffect 配对并具有依赖性以更新 ref.current 值以使其保持更新,而这,再一次,本质上让你得到 useMemo 钩子。

更新

由于您确实希望优化为组件的生命周期提供静态唯一 ID 的自定义挂钩:

  1. 使用useMemo

    const useUniqueId = (prefix = 'prefix_') => {
      return useMemo(() => uniqueId(prefix), []);
    };
    
  2. 使用useState

    const useUniqueId = (prefix = 'prefix_') => {
      const [uniqueId] = useState(() => uniqueId(prefix));
      return uniqueId;
    };
    

首先是简短的回答:如果我必须决定使用哪种机制,我会选择 useMemo:

const uniqueId = useMemo(() => getUniqueId('prefix_'), []);

它在这里做了我们想要的所有事情:getId 函数只被调用一次,因为依赖数组是空的,返回值是稳定的,它是成本高效的。没有与 useMemo 相关的神奇成本,计算是重还是轻都无关紧要。正如德鲁所说,它简洁明了。 自定义挂钩如下所示:

export const useUniqueId = () => {
  const uniqueId = useMemo(() => getUniqueId('prefix_'), []);
  return uniqueId;
}

较长的答案: 通常,如果您想要一个与渲染周期没有任何关系并且在组件的生命周期内保持稳定的值,我会选择 useRef。 但是 useRef 不像 useStateuseMemo 那样支持初始化函数。我不想在每个渲染器上调用 getUniqueID,因此将被迫将它与 useEffect 结合起来以初始化 ref。这在这里确实有点麻烦,所以我认为 useMemo 在这里完成了工作。

const uniqueId = useRef();
useEffect(() => { uniqueId.current = getUniqueId('prefix_') }, []);

请注意,使用 useEffect 的解决方案只会在渲染函数 运行 完成后提供值,如果您立即需要 uniqueID,这会造成麻烦,例如在某些元素上设置 HTML 属性 ID。