[英]React.memo vs useMemo for private components
想象一下,您有一个组件需要一个函数,该函数在该组件的层次结构中呈现特定的子树:
const Timeline = ({ renderDetails, ... }: { renderDetails: (rowData, sectionID, rowID) => React.Node }) => <div>{renderChildren(...)}</div>;
使用有没有区别:
const MyComponent = React.memo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);
const ParentComponent = ({ someProp }) => {
const renderChildren = useCallback(() => <MyComponent someProp={someProp} />, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
对比
const ParentComponent = ({ someProp }) => {
const MyComponent = useMemo(({ someProp }) => <someComponentHierarchyHere>...</someComponentHierarchyHere>);
const renderChildren = useCallback((rowData, sectionID, rowID) => <MyComponent someProp={someProp} />, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
或者简单地说:
const ParentComponent = ({ someProp }) => {
const renderChildren = useCallback((rowData, sectionID, rowID) => <someComponentHierarchyHere>...</someComponentHierarchyHere>, [someProp]);
return <Timeline renderChildren={renderChildren} />;
};
? 我对 React 渲染器将采取的决策以及MyComponent
组件(或它在组件树中插入的层次结构)是否会被记忆特别感兴趣。
通常,组件定义应该始终是静态的,因此React.memo
是这里的正确选择。
如果您的内部组件是完全无状态的,它似乎可以工作,但是您将无法在内部组件内部使用任何挂钩,因此对于任何复杂的事情,无论如何您都必须以其他方式进行。
这样做的原因是,在内部,React 会跟踪在任何给定时间正在渲染的组件,这就是它如何确定useState
和其他钩子返回的值的方式。 请注意,您没有在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 />;
}
两者具有相似的名称并且是优化方法。 但这就是相似性结束的地方,它们是完全不同的 React 特性。
React.memo
适用于当您发现现有组件的渲染成本很高,并且您无法在内部对其进行优化时。 把它包装起来,让 React 做一个额外的检查。
React.useMemo
用于通过保存昂贵函数调用的返回值来内部优化组件。
useMemo
源代码相对简单。 如果 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;
在所有其他情况下,它调用(据说很昂贵) nextCreate
函数。 结果首先保存在挂钩日志中(用于下一次渲染),然后返回。
对于初始(“mount”)渲染,React 使用了一个更简单的实现,它只调用nextCreate
,保存它,然后返回它。
React.memo
React.memo
是一种完全不同类型的函数。 它是一种高阶组件,但有一个问题。 它不会返回一个常规的 React 组件,而是一个特殊的REACT_MEMO_TYPE
。 这种类型最终会导致在执行渲染工作时对这些元素进行特殊处理。
这有更多的移动部分,但为了简单起见,我只是复制了 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);
}
与useMemo
不同,它不涉及存储任何额外的状态。 相反,比较函数用于避免调用(据说很昂贵)渲染函数。 它只是在开始之前中止一大块工作,当它发现它没有用时。
这些实际上是两个具有相似名称和目的的不同事物。
你可能需要React.memo
。
当然,您总是首先衡量是否有任何需要优化的地方。 这些事情中的每一个都有其自身的成本。 你不能把它洒在周围并自动获得性能。 如果没有通过避免昂贵的任务来平衡,它只会使代码变得更慢和更复杂。
以下没有做任何有用的事情:
useMemo(({ someProp }) => <someComponentHierarchy>...</someComponentHierarchy>)
如果你检查上面的 React 源代码,你会看到nextCreate
函数被调用时不带参数。
无论您在其中放入什么, useMemo
都不会像React.memo
那样对 Memoized 元素类型进行相同的检查,因此它不能用于相同的目的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.