useCallback会给你的代码带来什么?
上次我分享了关于 React.Memo的相关内容,有兴趣的可以去看一下,并且也欢迎大家对我的文章提出问题
组件涉及到的优化方式
为什么我们需要去优化React组件?,React在组件的渲染上会有什么问题?
- React组件有个特性,在不进行优化处理时父组件更新的时,子组件一定会重新渲染
优化的角度
- 减少组件的不必要渲染
- 提高组件的可读性以及减少复杂度,避免出现难以发现的bug
useCallback
useCallback
是一个允许你在多次渲染中缓存函数的 React Hook。
- 也就是说通过useCallback在依赖不发生变化的情况下我们得到的函数和上次的值保持同一引用地址的
const cachedFn = useCallback(fn, dependencies)
上代码
const Com2 = React.memo((props: any) => {
const [com2Data, setCom2Data] = useState(0);
console.log("Com2===> 重新渲染了");
return (
<>
<span>Com2的数据: {com2Data}</span>
<br />
<br />
<button
onClick={() => {
setCom2Data(com2Data + 1);
}}
>
点击Com2数据
</button>
</>
);
});
function App() {
const [appData, setAppData] = useState(0);
console.log("App===> 重新渲染了");
//没有使用useCallback🎈🎈🎈🎈🎈
const handleFun = () => {
console.log("我是一个函数");
};
return (
<div className="App">
<span>appData: {appData}</span>
<button
onClick={() => {
setAppData(appData);
}}
>
点击更改appData数据
</button>
<br />
<Com2 data={appData} handleFun={handleFun} />
</div>
);
}
- 点击更改appData的数据
来分析一下吧🥱🥱🥱🥱🥱🥱
. Com2组件是通过
React.Memo
包裹的所以组件是具有对接收到的props有对比的功能的,很明显这里失效了,为啥呢?
- 与字面量对象
{}
总是会创建新对象类似,在 JavaScript 中,function () {}
或者() => {}
总是会生成不同的函数。正常情况下,这不会有问题,但是这意味着Com2
props 将永远不会是相同的,- 这也就是
useCallback
存在的意义了
修改下代码
function App() {
const [appData, setAppData] = useState(0);
console.log("App===> 重新渲染了");
//使用useCallback🎈🎈🎈🎈🎈
const handleFun = useCallback(() => {
console.log("我是一个函数");
},[]);
return (
<div className="App">
<span>appData: {appData}</span>
<button
onClick={() => {
setAppData(appData);
}}
>
点击更改appData数据
</button>
<br />
<Com2 data={appData} handleFun={handleFun} />
</div>
);
}
- 一切又向我们所期待的方向发展了
是否应该在任何地方添加 useCallback
?
使用
useCallback
缓存函数仅在少数情况下有意义:
- 将其作为 props 传递给包装在 [
memo
] 中的组件。如果 props 未更改,则希望跳过重新渲染。缓存允许组件仅在依赖项更改时重新渲染。- 传递的函数可能作为某些 Hook 的依赖。比如,另一个包裹在
useCallback
中的函数依赖于它,或者依赖于useEffect
中的函数。
useCallback依赖组件中的某个状态值
// 方式一
function TodoList() {
const [todos, setTodos] = useState([]);
const handleAddTodo = useCallback((text) => {
const newTodo = { id: nextId++, text };
setTodos([...todos, newTodo]);
}, [todos]);
// ...
方式二
function TodoList() {
const [todos, setTodos] = useState([]);
const handleAddTodo = useCallback((text) => {
const newTodo = { id: nextId++, text };
setTodos(todos => [...todos, newTodo]);
}, []); // ✅ 不需要 todos 依赖项
// ...
推荐使用第二种方式
Effect频繁调用
当你在Effect中需要调用函数的时候
// 方式1
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
const createOptions = useCallback(() => {
return {
serverUrl: 'https://localhost:1234',
roomId: roomId
};
}, [roomId]); // ✅ 仅当 roomId 更改时更改
useEffect(() => {
const options = createOptions();
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // ✅ 仅当 createOptions 更改时更改
//....
//方式2
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
useEffect(() => {
const createOptions = () => {
return {
serverUrl: 'https://localhost:1234',
roomId: roomId
};
}; // ✅ 仅当 roomId 更改时更改
const options = createOptions();
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ 仅当 createOptions 更改时更改
//....
现在你的代码变得更简单了并且不需要 useCallback
推荐方式2
自定义hook
建议将它返回的任何函数包裹在
useCallback
中
备注重点
- 如果不传递第二依赖性,每次都将返回一个新的函数
useCallback
只应作用于性能优化。如果代码在没有它的情况下无法运行,请找到根本问题并首先修复它,然后再使用useCallback
useCallback
不会阻止创建函数。你总是在创建一个函数,但是如果没有任何东西改变,React 会忽略它并返回缓存的函数。
转载自:https://juejin.cn/post/7253267293223338044