掌握React的Usecallback,Usememo,Memoization和Closures
#javascript #react #hooks

我掌握React的Usecallback和Usememo的旅程

在不断发展的反应开发环境中,性能优化一直是我的一个令人着迷的难题。在这个领域中,理解记忆和关闭不仅是有益的。这是超级大国。在本文中,我想带您揭开这些概念的旅程,以及它们如何在我对React的钩子(尤其是useCallbackuseMemo)中的掌握中发挥作用。

回忆的启示

纪念活动的核心就像是为昂贵的功能调用提供秘密缓存。当您意识到可以存储和重复使用功能结果时,“啊哈”的时刻,这可能是绩效的救生员。在JavaScript中,这通常涉及基于其输入参数巧妙地存储函数返回值。让我们以一个简单的回忆示例来深入了解我的大开眼界:

function memoize(fn) {
  const cache = new Map();

  return function (...args) {
    const key = args.join('-');

    if (cache.has(key)) {
      return cache.get(key);
    } else {
      const result = fn(...args);
      cache.set(key, result);
      return result;
    }
  };
}

const add = (a, b) => a + b;
const memoizedAdd = memoize(add);

console.log(memoizedAdd(1, 2)); // Result is computed and cached
console.log(memoizedAdd(1, 2)); // Result is retrieved from cache

在此示例中, memoize 功能采用另一个函数 fn 并返回了它的记忆版本。缓存根据传递给 fn 的参数存储结果。当再次调用 memoizedAdd(1, 2) 时,它不会重新计算结果;相反,它从缓存中检索它。这可以显着提高计算上昂贵或具有相同输入输出模式的功能的性能。

闭合连接

封闭是一个基本的JavaScript概念,在 useCallback useMemo 的行为中起着核心作用。封闭就像JavaScript的隐藏宝石。这是功能能够记住过去的事情的原因,即使他们的外部功能早就在舞台上完成了表现。让我与您分享一个个人故事:

function outer() {
  const message = 'Hello, ';

  function inner(name) {
    console.log(message + name);
  }

  return inner;
}

const greet = outer();
greet('Saman'); // Outputs: Hello, Saman

在这个故事中, inner 函数“关闭” message 变量,即使在 outer 表示告别之后,也可以保留其内存。这个概念在 useCallback useMemo 中至关重要,因为它允许函数记住过去的价值观,即使跨越反应者。

React的 usecallback 中的封闭和回忆

现在,让我们快速前进我的反应之旅。 React的 useCallback 就像为您的回调功能拥有守护天使。想象一下这种情况:您的回调是作为孩子组成的道具。如果没有 useCallback ,每个父组件的每一个渲染都会产生一个新的回调,可能会导致孩子不必要的重新租赁。但是使用 useCallback ,魔术发生了:

const memoizedCallback = useCallback(() => {
  // Function logic
}, [dependency1, dependency2]);
  1. 创建和记忆回调函数。
  2. t坚持其依赖关系( dependency1 dependency2 )。
  3. 如果这些依赖关系不变,则返回相同的回调。
  4. 只要依赖关系保持恒定,这种超级英雄的举动可以防止不必要的子部分重新租赁。

React的 usememo 中的封闭和回忆

在我的反应之旅中, useMemo 是我值得信赖的搭档。它没有记忆功能,而是记忆价值。这对于重型计算特别有价值。让我分享它的工作方式:

const memoizedValue = useMemo(() => {
  // Value computation
  return someValue;
}, [dependency1, dependency2]);
  1. value 是计算和回忆的。
  2. 它紧贴其依赖项( dependency1 dependency2 )。
  3. 如果这些依赖性不变,则返回相同的记忆值。这对于避免冗余计算是很棒的。

我对 usecallback usememo 的英勇应用

既然您已经走进了我的React鞋子,让我们看看我如何使用 useCallback useMemo 在现实世界中节省一天:

方案1:优化事件处理程序

想象一个具有多个输入字段的复杂形式,每个字段都带有其事件处理程序进行更改。没有USECALLBACK,您将在每个渲染上创建新的事件处理程序功能。不好,对吗?

const MyForm = () => {
  const handleInputChange1 = useCallback((e) => {
    // Handle input change for field 1
  }, []);

  const handleInputChange2 = useCallback((e) => {
    // Handle input change for field 2
  }, []);

  // ... more fields

  return (
    <form>
      <input onChange={handleInputChange1} />
      <input onChange={handleInputChange2} />
      {/* ... more inputs */}
    </form>
  );
};

使用 useCallback ,我确保重复使用相同的事件处理程序功能,从而阻止不必要的我的形式重新租赁。就像拥有表格性能的超级大国一样。

方案2:回忆昂贵的计算

想象一下构建一个数据可视化组件,该组件根据用户输入执行复杂的计算。如果没有 useMemo ,即使输入没有更改,这些计算也会发生在每个渲染上。效率不高,对吗?

const DataVisualization = ({ data }) => {
  const computedData = useMemo(() => {
    // Expensive data processing based on 'data'
    return performComplexCalculations(data);
  }, [data]);

  return <Chart data={computedData} />;
};

使用 useMemo ,我确保只有在数据更改时才会发生昂贵的数据处理。这就像有一个个人数据男管家知道何时工作和何时休息。

方案3:防止不必要的渲染

在我的宏伟反应冒险中,我了解到,防止不必要的渲染在大规模应用中至关重要。想象一个父部分渲染子组件列表。如果没有 useMemo ,即使数据没有更改,将未倒置的道具传递给儿童组件也可能触发重新汇款。

const ParentComponent = ({ items }) => {
  const nonMemoizedItems = items; // Without useMemo

  return (
    <div>
      {nonMemoizedItems.map((item) => (
        <ChildComponent key={item.id} item={item} />
      ))}
    </div>
  );
};

通过利用 useMemo 的力量,我确保只有在实际数据发生变化时才能重新渲染孩子的组件,而不仅仅是当有人在房间里咳嗽时。

我旅程的表演智慧

与任何旅程一样,有宝贵的经验教训。让我与 useCallback useMemo

1.避免过早优化

作为英雄开发人员,很容易优化一切,但请记住智慧:“过早优化是万事万物的根源。”在应用回忆之前,请确定应用程序中的真实性能瓶颈。将超级大国集中在最重要的地方。

示例:考虑一个简单的组件,该组件呈现一个项目列表。如果列表很小并且不经常变化,则使用 useMemo 来记忆整个列表。在这种情况下,它是过早的优化。

const ItemList = ({ items }) => {
  const memoizedItems = useMemo(() => items, [items]);

  return (
    <ul>
      {memoizedItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
};

2.介意计算开销

回忆很棒,但不是免费的。存储和检查缓存值的行为增加了自己的计算开销。确保您的优化实际上在性能方面很重要。对于快速而简单的计算,回忆可能过于杀伤。

示例:考虑组件内的基本计算。纪念它可能无法提供显着的性能提高,尤其是在计算很简单并且不会消耗很多资源的情况下。

const SimpleCalculation = ({ value }) => {
  const memoizedResult = useMemo(() => value * 2, [value]);

  return <div>Result: {memoizedResult}</div>;
};

3.观看这些依赖阵列

依赖性阵列是 useMemo useCallback 中的指导星。让他们苗条和卑鄙。复杂的依赖阵列会导致不必要的重新计算和混乱。请记住,更少

示例:如果依赖性数组变得太长或包含依赖性实际不会影响回忆价值或功能的行为,则可能导致过度重新计算。

const ComplexDependencyArray = ({ data, filter, someOtherVariable }) => {
  const filteredData = useMemo(() => {
    // Expensive data filtering logic
    return data.filter(item => item.name.includes(filter));
  }, [data, filter, someOtherVariable]);

  return <ul>{filteredData.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
};

在此示例中,包括 someOtherVariable 在依赖性数组中可能会导致不必要的重新计算,如果它不影响过滤逻辑。

4.考虑参考身份与价值平等

回忆取决于参考身份。在处理复杂的数据结构时,这可能是一种祝福或诅咒。确保影响参考身份的方面比影响价值平等的方面更重要。

const ComplexDataStructure = ({ data }) => {
  const memoizedData = useMemo(() => data, [data]);

  // ...

  return <div>Data Length: {memoizedData.length}</div>;
};

如果 data 是一个数组,可以通过修改其元素而不更改参考的元素来更新, memoizedData 不会反映这些更改。

5.拥抱维护复杂性

备忘录为您的代码增加了复杂性。拥抱它,但不要忘记记录您的回忆价值和功能。未来的开发人员(包括未来)将感谢指导。

// This memoized function is used to calculate the total price of items in the cart.
const calculateTotalPrice = useMemo(() => {
  // Expensive calculation logic
  return items.reduce((total, item) => total + item.price, 0);
}, [items]);

此评论有助于未来的开发人员了解记忆的目的。

6.当心与和解的权衡

React的对帐过程已优化,以提高效率。过度使用 useCallback 可能导致内存问题,因为保留的参考可能会阻止对象被垃圾收集。

React中不正确使用 usememo usecallback 的成本

滥用 useMemo useCallback 可能会导致意想不到的后果,尤其是在记忆使用方面。让我分享潜在的陷阱和相关的内存成本。

1.不必要的回忆

无缘无故地应用Usememo和Usecallback就像ho积旧杂志一样。不要记忆不需要的值或功能。 React的对帐可以有效地处理对道具的更新。
考虑此示例:

const Component = ({ data }) => {
  const memoizedData = useMemo(() => data, [data]);

  // Component logic
};

在这种情况下, data 已经是对象或数组的引用。纪念它没有任何好处,因为React的对帐过程有效地处理了道具的更新。纪念数据在这里消耗其他内存而没有正当理由。

2.过度使用回忆

在整个应用程序中,过度使用 useMemo useCallback 可能会导致记忆膨胀。每个回忆的值或回调都会消耗内存。优先考虑最重要的优化。

3.依赖阵列过多

依赖性阵列决定 useMemo useCallback 重新计算。太多的依赖项会导致不必要的重新计算,从而影响内存使用情况。让他们简洁。
考虑此示例:

const Component = ({ data, filter }) => {
  const filteredData = useMemo(() => {
    // Expensive data filtering logic
    return data.filter(item => item.name.includes(filter));
  }, [data, filter, someOtherVariable]);

  // Component logic
};

在这种情况下, filteredData 取决于 data filter someOtherVariable 。如果 someOtherVariable 不影响过滤操作的结果,则应将其从依赖性数组中删除,以避免不必要的重新计算和内存使用。

4.内存泄漏

滥用 useMemo useCallback 会导致内存泄漏。当您记住捕获过时引用的值或回调时,这些对象无法收集垃圾。
例如:

const Component = () => {
  const handleClick = useCallback(() => {
    // Click handler logic
  }, []);

  useEffect(() => {
    // Adding an event listener
    window.addEventListener('click', handleClick);

    return () => {
      // Removing the event listener
      window.removeEventListener('click', handleClick);
    };
  }, [handleClick]);

  // Component logic
};

在此示例中, handleClick 函数通过空的依赖性数组进行记忆,这意味着它是一个常数引用。如果此组件卸下,则删除了事件的侦听器,但是对 handleClick 的引用持续存在,可能导致内存泄漏。

综上所述

我掌握 useCallback useMemo 的旅程一直在启发。这些概念使我从一个反应爱好者变成了反应超级英雄。请记住,性能优化是提高速度和维护代码简单性之间的舞蹈。有了深刻的了解,您可以在项目中有效地有效地了解 useCallback useMemo 现在,轮到您开始自己的反应冒险了。带上这些超级大国,明智地挥动它们,并使您的反应应用飞行。表演世界正在等待您的英勇壮举。

其他资源

愉快的编码,其他反应超级英雄!