React.memo
// Without Memoization
const MyComponent = ({ data }) => {
// Component logic
return <div>{data}</div>;
};
// With Memoization
import React from "react";
const MemoizedComponent = React.memo(({ data }) => {
// Component logic
return <div>{data}</div>;
});
export default MemoizedComponent;
列表渲染
import React, { memo } from "react";
// MemoizedComponent using react.memo
const MemoizedComponent = memo(({ data }) => {
console.log("Rendering MemoizedComponent");
return <li>{data}</li>;
});
// ItemList using MemoizedComponent
const ItemList = ({ items }) => {
return (
<ul>
{items.map((item) => (
<MemoizedComponent key={item.id} data={item.data} />
))}
</ul>
);
};
export default ItemList;
MemoizedComponent
用react.memo
包裹起来,它执行道具的浅层比较,以防止不必要的重新渲染。
功能性道具
import React, { memo } from "react";
// MemoizedComponent using react.memo
const MemoizedComponent = memo(({ data }) => {
console.log(`Rendering MemoizedComponent for data: ${data}`);
return <div>{data}</div>;
});
// UserDetails component
const UserDetails = ({ user }) => {
return (
<div>
<MemoizedComponent data={user.name} />
<MemoizedComponent data={user.email} />
</div>
);
};
// App component
const App = () => {
const user1 = { name: "John Doe", email: "john@example.com" };
const user2 = { name: "Jane Doe", email: "jane@example.com" };
return (
<div>
<h1>User Details - Memoization Example</h1>
<UserDetails user={user1} />
<UserDetails user={user2} />
</div>
);
};
export default App;
MemoizedComponent是
一个使用 react.memo
优化的功能组件,它允许它根据 data 属性的变化通过记忆其实例来进行渲染。该UserDetails
组件使用MemoizedComponent
两次,每次使用来自用户属性的不同数据。该App
组件通过渲染两组UserDetails
不同的用户对象来演示记忆行为,展示记忆如何在组件接收到不同数据时防止不必要的重新渲染。
当函数作为 props 传递给子组件时,如何使用useCallback()
钩子避免重新渲染。
如果我们将函数作为 prop 传递给子组件,即使在使用之后React.memo()
,子组件也会重新渲染。
示例:
// Parent.tsx
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
const handler = () => {
console.log("handler"); // This is the new handler that will be passed to the child
};
console.log("Parent render");
return (
<div className="App">
<button onClick={handleClick}>Increment</button>
<h2>{count}</h2>
<Child name={"joe"} childFunc={handler} />
</div>
);
}
export default Parent;
// Child.tsx
export const Child = (props) => {
console.log("Child render");
return (
<div>
<h2>{props.name}</h2>
</div>
);
}
export default React.memo(Child);
现在,当我们增加父组件中的计数时,它会重新渲染并重新渲染子组件,即使传递的 props 没有变化。
那么,是什么导致了孩子重新渲染呢?答案是,每次父组件重新渲染时,都会创建一个新的处理函数并将其传递给子组件。现在,由于每次重新渲染时都会重新创建处理程序函数,因此子组件在对 props 进行浅比较时会发现处理程序引用已更改并重新渲染子组件。
useCallback()
避免进一步重新渲染
导致子级重新渲染的主要问题是处理程序函数的重新创建,该函数更改了传递给子级的引用。因此,我们需要有一种方法来避免这种娱乐。如果未重新创建处理程序,则对处理程序的引用不会更改 - 因此子级不会重新渲染。
为了避免每次渲染父组件时都重新创建该函数,我们将使用一个名为useCallback()的 React hook 。Hooks 是在 React 16 中引入的。要了解有关 Hooks 的更多信息,您可以查看 React 的官方Hooks 文档,或者查看“ React Hooks: How to Get Started & Build Your Own ”。
该useCallback()
钩子有两个参数:回调函数和依赖项列表。
考虑以下useCallback(
) 的示例:
const handleClick = useCallback(() => {
//Do something
}, [x,y]);
这里,useCallback()
被添加到handleClick()
函数中。第二个参数[x,y]
可以是空数组、单个依赖项或依赖项列表。每当第二个参数中提到的任何依赖项发生变化时,只有那时才会handleClick()
重新创建该函数。
如果 中提到的依赖项useCallback()
没有更改,则返回作为第一个参数提到的回调的记忆版本。我们将更改父功能组件以使用useCallback()
传递给子组件的处理程序的钩子:
//Parent.tsx
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
// useCallback(()=>{},[])
const handler = useCallback(() => { //using useCallback() for the handler function
console.log("handler");
}, []);
console.log("Parent render");
return (
<div className="App">
<button onClick={handleClick}>Increment</button>
<h2>{count}</h2>
<Child name={"joe"} childFunc={handler} />
</div>
);
}
export default Parent;
由于我们useCallback()
对父处理程序使用了钩子,因此每次父处理程序重新渲染时,处理程序函数都不会被重新创建,并且处理程序的记忆版本会被发送给子处理程序。子组件将进行浅层比较,并注意到处理程序函数的引用没有更改 - 因此它不会调用该render
方法。
状态管理优化
React 中的状态管理优化是指提高 React 应用程序中管理状态的效率和性能。React 应用程序使用状态来表示用户界面的动态方面。
React 提供了两个主要的钩子来管理功能组件中的状态:useState
和useReducer
。这些挂钩允许您创建和管理本地组件状态。
使用示例useState
:
import React, { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
Counter()
使用钩子useState
来管理数字count
状态。该组件呈现一个显示当前按钮和两个按钮的段落,count
允许用户increment
或decrement
. count
和increment
函数decrement
用于setCount()
根据当前状态更新状态count
。
使用示例useReducer
:
import React, { useReducer } from "react";
const counterReducer = (state, action) => {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
const increment = () => dispatch({ type: "INCREMENT" });
const decrement = () => dispatch({ type: "DECREMENT" });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
使用分派操作处理counterReducer()
状态更新,从而实现动态计数递增和递减。该组件呈现一个显示当前计数的段落和两个按钮,允许用户通过dispatch()
.
本地状态的优化使用
状态管理可以通过最小化状态更改来帮助您充分利用本地状态。
最小化状态变化有助于避免不必要的渲染。确保状态更新仅在需要时发生。在处理复杂的状态对象或数组时,这一点至关重要。
例子:
import React, { useState } from "react";
const ComplexStateComponent = () => {
const [user, setUser] = useState({ name: "", age: 0 });
const updateName = (name) => setUser({ ...user, name });
const updateAge = (age) => setUser({ ...user, age });
return (
<div>
<input
type="text"
placeholder="Name"
value={user.name}
onChange={(e) => updateName(e.target.value)}
/>
<input
type="number"
placeholder="Age"
value={user.age}
onChange={(e) => updateAge(e.target.value)}
/>
</div>
);
};
ComplexStateComponent()使用
useState
来管理用户信息的复杂状态对象(name
和age
)。呈现两个输入字段以更新用户的姓名和年龄。该组件利用函数 (updateName
和updateAge
) 来更新用户对象的特定属性,通过传播现有状态来确保不变性。
优化本地状态管理对React组件的性能有直接影响。通过最大限度地减少不必要的状态更新并确保状态更改仅在必要时触发,开发人员可以提高应用程序的效率。它可以缩短渲染时间并提高用户界面的响应速度。
延迟加载和代码分割
延迟加载是一种仅在需要时加载资源(例如数据或代码)的技术,而不是在开始时加载所有内容。
代码拆分是一种通过将代码分解为更小、更易于管理的块来提高 Web 应用程序性能和加载时间的策略。
这两种策略都允许您仅加载必要的组件和资源。该React.lazy
功能和Suspense
组件有助于实现这一点:
const MyLazyComponent = React.lazy(() => import("./MyComponent"));
// Usage
<Suspense fallback={<div>Loading...</div>}>
<MyLazyComponent />
</Suspense>;
React.lazy函数
使用动态导入
React.lazy
在 React 中启用动态代码分割。它允许您异步加载组件,从而缩短应用程序的初始加载时间。
例子:
import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
const MyParentComponent = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
LazyComponent
仅在渲染时加载,MyParentComponent
减少初始包大小并提高应用程序的启动性能。
使用异步加载
异步加载是React.lazy
. React.lazy
允许通过动态导入来异步加载组件import()
。这意味着主线程仍然可以自由地处理其他任务,从而防止应用程序在加载过程中变得无响应。
异步加载示例:
import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
const MyParentComponent = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
浏览器可以LazyComponent
在后台加载时继续执行其他脚本并处理用户交互。
优化计算
在处理计算量大的操作(例如排序或过滤大型数组)时,记忆化变得很有价值。
示例:
import React, { useMemo } from "react";
const SortingComponent = ({ data }) => {
const sortedData = useMemo(() => {
return [...data].sort((a, b) => a - b);
}, [data]);
return (
<ul>
{sortedData.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
};
存储sortedData
数组可以保证在数据依赖性发生变化时重新计算。
通过钩子进行记忆化useMemo
是一种很有价值的技术,可以通过仅在依赖项发生变化时选择性地重新计算值来优化 React 组件。