根据我从 React 文档和 Web 上其他材料中了解到的情况,useCallback 用于通过确保将回调的记忆版本传递给它来避免子组件的重新渲染,因此子组件的引用道具保持不变。但所有这些只有在我在子组件上使用 React.memo 时才有效。如果没有 React.memo,子组件无论如何都会重新渲染。我的问题是在这种情况下useCallback有什么用,即没有将React.memo应用于子组件。useCallback 还有什么其他好处?
React.memo 确保在 props 进入组件时执行浅层比较,并在它们相等时跳过组件的渲染。
给定一个子组件 Cell: 当应用于在渲染另一个父组件期间创建的函数组件时,将在每次渲染时创建一个新的 Cell 组件。这个新的 Cell 组件将始终在其道具上进行浅层比较,但它将在其父级的每个渲染中重新渲染。
但是,useCallback 将记住此函数回调 Cell,如果它的依赖数组在父重新渲染期间没有更改。 单独包装在useCallback中的函数组件Cell在收到props时将始终重新渲染,这将在其父级的每次渲染中发生。 然而,不同之处在于,如果重新创建组件本身,它的整个子树都会重新渲染,就像单独使用 React.memo 时一样。
但是,配对在一起,您可以绕过重新渲染父级内部定义的组件。
正如您会注意到的,当尝试拖动记忆单元格时,未包装在useCallback中的单元格不会发生拖动。这是因为您尝试拖动的原始元素在父元素重新渲染时会重新创建为新实例。 这里更详细地解释了这个概念
例
const { useCallback, useState, useRef } = React;
function App() {
const [state, setState] = useState(false);
const [title, setTitle] = useState("");
const Cell = useCallback(({ title }) => {
console.log(`${title} rendering`);
function onDragStart() {
setState(true);
}
function onDragEnd() {
setState(false);
}
return (
<div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
Drag {title}
</div>
);
}, []);
const MemoizedCell = React.memo(({ title }) => {
console.log(`${title} rendering`);
function onDragStart() {
setState(true);
}
function onDragEnd() {
setState(false);
}
return (
<div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
Drag {title}
</div>
);
});
const MemoizedCallbackCell = useCallback(
React.memo(({ title }) => {
console.log(`${title} rendering`);
function onDragStart() {
setState(true);
}
function onDragEnd() {
setState(false);
}
return (
<div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
Drag {title}
<button onClick={() => setTitle(" Updated")}>Change Title</button>
</div>
);
}),
[]
);
return (
<div className="App">
<Cell title="Cell" />
<MemoizedCell title="Memoized Cell" />
<MemoizedCallbackCell title={"Memoized Callback Cell" + title} />
Is dragging {`${state}`}
<br />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
您可能还需要将函数传递给useEffect
,而无需更改每个渲染的useEffect
例如:
import { useCallback, useEffect } from "react";
function useExample() {
function fn() {
console.log("a function");
}
const callback = useCallback(fn, []);
useEffect(() => {
callback();
}, [callback]);
}
具有useDebounce
的真实示例: