【问题标题】:What are production use cases for the useRef, useMemo, useCallback hooks?useRef、useMemo、useCallback 钩子的生产用例是什么?
【发布时间】:2021-05-31 09:28:58
【问题描述】:

除了在许多 YouTube 教程视频中看到的 counter 示例之外,useMemouseCallback实际/真实用例是什么?

另外,我只看到了useRef 钩子的输入焦点示例。

请分享您为这些钩子找到的其他用例。

【问题讨论】:

  • useRef 可用于在组件中存储本地可变值。它不参与重新渲染(unline state data)。 useMemo 用于记忆(就像我们在动态编程中所做的那样,概念明智)并跳过重新计算。当您不想在每次渲染组件时重新计算繁重的计算时,它很有用。 useCallback 用于避免在每次渲染时重新创建/重新定义方法。
  • 只是想了解您的基本原理。我认为你提到的那些用例和任何用例一样真实。而且,如果您无法将它们的用途外推到实际的应用程序中……好吧,我怀疑有人会给您提供有关如何正确使用它们的诊所。 Ajeet 的肤浅评论几乎是最好的。
  • 例如,您有一些处于状态的数据,例如,包含 100 个对象的数组,并且您希望在 UI 上显示该数据之前进行一些过滤和排序。如果您不 memoize this 排序/过滤计算,则每次组件重新渲染时都会完成(即使其他状态变量发生变化)。
  • @AjeetShah 正是我想要的。如果您想将此作为答案而不是评论进行回复,我会将其标记为正确。再次感谢,Ajeet!
  • @AjeetShah ????????

标签: reactjs react-hooks use-ref usecallback react-usememo


【解决方案1】:

我想补充一点,对于 useMemo,我通常在我想同时结合 useState 和 useEffect 时使用它。例如:

...
const [data, setData] = useState(...);
const [name, setName] = useState("Mario");
// like the example by ajeet, for complex calculations
const formattedData = useMemo(() => data.map(...), [data])
// or for simple state that you're sure you would never modify it directly
const prefixedName = useMemo(() => NAME_PREFIX + name, [name]);

我不知道是否会有性能问题,因为文档声明 useMemo 应该用于昂贵的计算。但我相信这比使用 useState 更干净

【讨论】:

    【解决方案2】:

    useRef:

    语法: const refObject = useRef(initialValue);

    它只是返回一个普通的 JavaScript object。可以根据需要多次访问和修改其值(mutability),而无需担心“重新渲染”。

    它的值将persist(不会被重置为initialValue,这与函数组件中定义的普通*对象不同;它会持续存在,因为useRef为您提供了相同的对象,而不是创建在后续渲染中添加一个新的)用于组件生命周期。

    如果您在控制台上写入const refObject = useRef(0) 并打印refObject,您将看到日志对象-{ current: 0 }

    *普通对象 vs refObject,示例:

    function App() {
      const ordinaryObject = { current: 0 } // It will reset to {current:0} at each render
      const refObject = useRef(0) // It will persist (won't reset to the initial value) for the component lifetime
      return <>...</>
    }
    

    几个常见的用法,例子:

    1. 访问DOM&lt;div ref={myRef} /&gt;
    2. 存储可变值,例如instance variable (in class)
    3. 渲染counter
    4. setTimeout / setInterval 中使用的值,没有stale closure 问题。

    useMemo:

    语法const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a, b), [a, b]);

    它返回一个memoized 。这个钩子的主要目的是“性能优化”。在需要时谨慎使用它来优化性能。

    它接受两个参数——“create”函数(它应该返回一个要记忆的值)和“dependency”数组。只有当其中一个依赖项发生变化时,它才会重新计算记忆值。

    几个常见的用法,例子:

    1. 在渲染时优化昂贵的计算(例如对数据的操作,如排序、过滤、更改格式等)

    未记忆的例子:

    function App() {
      const [data, setData] = useState([.....])
    
      function format() {
        console.log('formatting ...') // this will print at every render
        const formattedData = []
        data.forEach(item => {
          const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
          if (newItem) {
            formattedData.push(newItem)
          }
        })
        return formattedData
      }
    
      const formattedData = format()
    
      return <>
        {formattedData.map(item => <div key={item.id}>
          {item.title}
        </div>)}
      </>
    }
    

    记忆示例:

    function App() {
      const [data, setData] = useState([.....])
    
      function format() {
        console.log('formatting ...') // this will print only when data has changed
        const formattedData = []
        data.forEach(item => {
          const newItem = // ... do somthing here, formatting, sorting, filtering (by date, by text,..) etc
          if (newItem) {
            formattedData.push(newItem)
          }
        })
        return formattedData
      }
    
      const formattedData = useMemo(format, [data])
    
      return <>
        {formattedData.map(item => <div key={item.id}>
          {item.title}
        </div>)}
      <>
    }
    

    useCallback:

    语法const memoizedCallback = useCallback(() =&gt; { //.. do something with a &amp; b }, [a, b])

    它返回一个memoized 函数(或回调)。

    它接受两个参数——“函数”和“依赖”数组。只有当其中一个依赖项发生变化时,它才会返回新的,即重新创建的函数,否则它将返回旧的,即记忆的函数。

    几个常见的用法,例子:

    1. 将 memoized 函数传递给子组件(通过 React.memoshouldComponentUpdate 使用浅相等 - Object.is 进行优化)以避免由于作为 props 传递的函数而不必要地重新渲染子组件。

    示例 1,没有 useCallback

    const Child = React.memo(function Child({foo}) {
      console.log('child rendering ...') // Child will rerender (because foo will be new) whenever MyApp rerenders
      return <>Child<>
    })
    
    function MyApp() {
      function foo() {
        // do something
      }
      return <Child foo={foo}/>
    }
    

    示例 1,useCallback

    const Child = React.memo(function Child({foo}) {
      console.log('child rendering ...') // Child will NOT rerender whenever MyApp rerenders
      // But will rerender only when memoizedFoo is new (and that will happen only when useCallback's dependency would change)
      return <>Child<>
    })
    
    function MyApp() {
      function foo() {
        // do something
      }
      const memoizedFoo = useCallback(foo, [])
      return <Child foo={memoizedFoo}/>
    }
    
    1. 将记忆函数作为依赖项传递给其他钩子。

    示例 2,没有 useCallback,不好(但 eslint-plugin-react-hook 会警告您纠正它):

    function MyApp() {
      function foo() {
        // do something with state or props data
      }
      useEffect(() => {
        // do something with foo
        // maybe fetch from API and then pass data to foo
        foo()
      }, [foo])
      return <>...<>
    }
    

    示例 2,useCallback,好:

    function MyApp() {
      const memoizedFoo = useCallback(function foo() {
        // do something with state or props data
      }, [ /* related state / props */])
    
      useEffect(() => {
        // do something with memoizedFoo
        // maybe fetch from API and then pass data to memoizedFoo
        memoizedFoo()
      }, [memoizedFoo])
      return <>...<>
    }
    

    这些钩子规则或实现将来可能会发生变化。所以,请务必检查文档中的hooks reference。此外,请务必注意关于依赖关系的 eslint-plugin-react-hook 警告。如果省略这些钩子的任何依赖,它将指导您。

    【讨论】:

    • 我很感激,Ajeet!感谢您提供用例!
    猜你喜欢
    • 2019-08-20
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    • 2020-06-01
    • 2022-12-18
    • 1970-01-01
    • 2019-08-14
    相关资源
    最近更新 更多