useState、UseRef 或 useMemo,我应该选择哪个

Ash*_*ish 5 javascript memoization reactjs react-hooks

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

考虑下面的例子

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>
    )
}
Run Code Online (Sandbox Code Playgroud)

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

我的理解:

useState应用于存储值的变化应触发重新渲染的值。

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

所以,useRef我认为是合适的。

但是,我有一个困惑:

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

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

更新

我想实现什么目标?

我想创建一个应该返回的自定义挂钩,uniqueId该挂钩在重新渲染时不应更改

const UniqueId = () {
    const uniqueId = useStableUniqueId('prefix__')
    return <div>{uniqueId}<div>
}
Run Code Online (Sandbox Code Playgroud)

因此,无论 UniqueId 重新渲染多少次,该值都不应改变。

Dre*_*ese 3

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

我有一个场景,我需要在整个组件生命周期中存储函数的输出。

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

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

如果您决定将其存储在 React ref 中,那么您将再次需要将其与useEffect依赖项配对以更新ref.current值以保持更新,这再次本质上让您上钩useMemo。

更新

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

  1. 使用useMemo

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

    const useUniqueId = (prefix = 'prefix_') => {
      const [uniqueId] = useState(() => uniqueId(prefix));
      return uniqueId;
    };
    
    Run Code Online (Sandbox Code Playgroud)

  • 很好的答案 - 一如既往 - 但我有一个但是...“传递给 useRef 的值只是初始值,所以如果它是函数调用,它只会被调用一次。” 关于这句话,您的答案不正确。如果我将函数传递给“useRef”,则该函数永远不会被调用,而只是存储为对函数的引用。如果我将函数调用的结果传递给“useRef”,则该函数会在每次渲染时调用。OP 对这一事实的评估是正确的。很快就会写出我的答案。 (2认同)
  • @DrewReese 是的,我检查了代码,并且刚刚检查了文档以确保每次组件重新呈现函数时都使用 `useRef(uniqueId('prefix_'))` (实际上两个函数: `useRef()`并再次调用`uniqueId()`)。只有 useRef 才会在第一次调用后丢弃任何初始化值。但它们确实被调用了。 (2认同)