简体   繁体   English

私有组件的 React.memo 与 useMemo

[英]React.memo vs useMemo for private components

Imagine you have a component expecting a function that renders a specific subtree in that component's hierarchy:想象一下,您有一个组件需要一个函数,该函数在该组件的层次结构中呈现特定的子树:

const Timeline = ({ renderDetails, ... }: { renderDetails: (rowData, sectionID, rowID) => React.Node }) => <div>{renderChildren(...)}</div>;

Is there a difference in using:使用有没有区别:

const MyComponent = React.memo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);

const ParentComponent = ({ someProp }) => {
    const renderChildren = useCallback(() => <MyComponent someProp={someProp} />, [someProp]);

    return <Timeline renderChildren={renderChildren} />;
};

vs对比

const ParentComponent = ({ someProp }) => {
    const MyComponent = useMemo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);
    const renderChildren = useCallback((rowData, sectionID, rowID) => <MyComponent someProp={someProp} />, [someProp]);

    return <Timeline renderChildren={renderChildren} />;
};

or simply:或者简单地说:

const ParentComponent = ({ someProp }) => {
    const renderChildren = useCallback((rowData, sectionID, rowID) => <someComponentHierarchyHere>...</someComponentHierarchyHere>, [someProp]);

    return <Timeline renderChildren={renderChildren} />;
};

? ? I'm specifically interested in the decisions React renderer will take, as well as whether the MyComponent component (or the hierarchy it inserts in the component tree) will be memoized.我对 React 渲染器将采取的决策以及MyComponent组件(或它在组件树中插入的层次结构)是否会被记忆特别感兴趣。

In general, component definitions should always be static, so React.memo is the correct option here.通常,组件定义应该始终是静态的,因此React.memo是这里的正确选择。

If your inner component is completely stateless, it will appear to work, but you won't be able to use any hooks inside of the inner component, so for anything complex, you would have to do it the other way anyhow.如果您的内部组件是完全无状态的,它似乎可以工作,但是您将无法在内部组件内部使用任何挂钩,因此对于任何复杂的事情,无论如何您都必须以其他方式进行。

The reason for this is that, internally, React keeps track of what component is rendering at any given time, and that's how it determines what values are returned by useState and other hooks.这样做的原因是,在内部,React 会跟踪在任何给定时间正在渲染的组件,这就是它如何确定useState和其他钩子返回的值的方式。 Note that you're not using a dependencies array in your useMemo , meaning that it will be recreated on every render, so it's effectively pointless (the react hooks linting rules will warn you about this. However, even with an empty dependencies array, it still would not be safe to use hooks in it. A common misconception is that is useMemo with no dependencies will create a constant, but this is not the case. It's an optimization that usually prevents recalculation of values, but it's not guaranteed. Internally, React can choose to discard the memoized result of useMemo at any time. If that happened, it would randomly discard your state, so React prohibits it entirely.请注意,您没有在useMemo中使用依赖项数组,这意味着它将在每次渲染时重新创建,因此它实际上毫无意义(react hooks linting 规则会警告您这一点。但是,即使使用一个空的依赖项数组,它在其中使用钩子仍然不安全。一个常见的误解是,没有依赖关系的useMemo会创建一个常量,但事实并非如此。这是一种通常会阻止重新计算值的优化,但不能保证。在内部, React 可以随时选择丢弃 useMemo 的useMemo结果,如果发生这种情况,它会随机丢弃你的状态,因此 React 完全禁止它。

import { useMemo, useState } from 'react';

export default function App() {
  const MyComponent = useMemo(() => {
    // This line errors:
    // React Hook "useState" cannot be called inside a callback.
    // React Hooks must be called in a React function component or a
    // custom React Hook function. (react-hooks/rules-of-hooks) eslint
    const [input, setInput] = useState('')
  
    return (
      <input value={input} onChange={(e) => setInput(e.target.text)} />
    )
  })

  return <MyComponent />;
}

Both have a similar name and are optimization methods.两者具有相似的名称并且是优化方法。 But that's where the similarity ends, they're totally distinct React features.但这就是相似性结束的地方,它们是完全不同的 React 特性。

React.memo is for when you find an existing component that is expensive to render, and you don't have the option to optimize it internally. React.memo适用于当您发现现有组件的渲染成本很高,并且您无法在内部对其进行优化时。 Just wrap it and let React do an extra check.把它包装起来,让 React 做一个额外的检查。

React.useMemo is for internally optimizing a component by saving the return value of an expensive function call. React.useMemo用于通过保存昂贵函数调用的返回值来内部优化组件。

Implementation执行

useMemo

The source code is relatively straightforward. 源代码相对简单。 The previous value is returned if the deps didn't change.如果 deps 没有改变,则返回之前的值。

const prevDeps: Array<mixed> | null = prevState[1];
if (prevState !== null) {
    if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
    }
}

const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;

In all other cases it calls the (supposedly expensive) nextCreate function.在所有其他情况下,它调用(据说很昂贵) nextCreate函数。 The result is first saved in the hook log (for use in next renders) and then returned.结果首先保存在挂钩日志中(用于下一次渲染),然后返回。

For the initial ("mount") render, React uses a simpler implementation that just calls nextCreate , saves it, and returns it.对于初始(“mount”)渲染,React 使用了一个更简单的实现,它只调用nextCreate ,保存它,然后返回它。

React.memo

React.memo is quite a different type of function. React.memo是一种完全不同类型的函数。 It's a sort of Higher Order Component, but with a catch.它是一种高阶组件,但有一个问题。 It doesn't return a regular React component, but a special REACT_MEMO_TYPE .它不会返回一个常规的 React 组件,而是一个特殊的REACT_MEMO_TYPE This type ultimately results in special handling of these elements while executing render work.这种类型最终会导致在执行渲染工作时对这些元素进行特殊处理。

There's more moving parts to this but for the sake of simplicity I just copied the props check .这有更多的移动部分,但为了简单起见,我只是复制了 props check

let compare = Component.compare; // This is what you pass into 2nd argument.
compare = compare !== null ? compare : shallowEqual;

if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
  return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}

Unlike useMemo it doesn't involve storing any additional state.useMemo不同,它不涉及存储任何额外的状态。 Instead the comparison function is used to avoid calling the (supposedly expensive) render function.相反,比较函数用于避免调用(据说很昂贵)渲染函数。 It simply aborts a chunk of work before starting it, when it sees it would be useless.它只是在开始之前中止一大块工作,当它发现它没有用时。

Conclusion结论

These are really 2 distinct things with a similar name and purpose.这些实际上是两个具有相似名称和目的的不同事物。

Applicability to OP use case适用于 OP 用例

You'll probably want React.memo .你可能需要React.memo

Of course you always first measure if there's anything to optimize at all.当然,您总是首先衡量是否有任何需要优化的地方。 Each of these things has a cost on its own.这些事情中的每一个都有其自身的成本。 You can't sprinkle it around and automatically gain performance.你不能把它洒在周围并自动获得性能。 If it's not balanced by avoiding expensive tasks, it's only making the code slower and more complex.如果没有通过避免昂贵的任务来平衡,它只会使代码变得更慢和更复杂。

The following doesn't do anything useful:以下没有做任何有用的事情:

useMemo(({ someProp }) => <someComponentHierarchy>...</someComponentHierarchy>)

If you check the React source above, you see the nextCreate function is called without arguments.如果你检查上面的 React 源代码,你会看到nextCreate函数被调用时不带参数。

Regardless of what you put in it, useMemo will not result in the same check for the Memoized element type as React.memo does, so it can't be used for the same purpose.无论您在其中放入什么, useMemo都不会像React.memo那样对 Memoized 元素类型进行相同的检查,因此它不能用于相同的目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM