你好,前端开发爱好者。
随着我对 React
的研究越来越深入,我越来越喜欢这款优秀的前端 JavaScript 库。无需多言,React 是当今最实用和最受开发者欢迎的工具之一。近来,React 团队所做的改进不仅对开发者产生了深远影响,也使得使用 React
构建的应用程序对用户更加友好。
今天,我跟大家分享一些在使用 React
构建应用程序时学到的重要经验,这些经验尤其基于 React Hooks
的使用进行了优化。我们将揭开一些常见的误解,简化常见的概念,并优化你的代码以达到最佳性能。
阅读本文的前提条件是:你必须至少使用过一次 React Hooks
,即用 React 开发过一个应用,并运用了 Hooks 的强大功能。这篇文章适合希望充分发挥 React Hooks
潜力的所有人。
React 状态必须是不可变的
你是否想过为什么 React 对不可变性如此执着?作为新手,你可能觉得 JavaScript 的可变性没什么问题,毕竟我们可以轻松地添加或删除对象属性,操作数组。
但在 React 中,不可变性不意味着状态永不改变,而是确保数据的一致性。如果你直接修改状态,React 无法可靠地检测出变化,可能导致 UI 没有按预期更新。正确的做法是用新数据副本替换旧数据。
示例代码:
const updatedUsers = [...users, newUser];
setUsers(updatedUsers);
以上代码使用扩展操作符创建了一个包含新用户的新数组 updatedUsers
,而不是直接修改原数组 users
。这种方法既确保了 React 的不可变性,也保证了组件的平滑更新。
不要把所有数据都用 useState
管理
曾几何时,我会毫不犹豫地把所有数据都丢进 useState
。然而,状态虽然强大,但滥用会导致代码变得复杂和低效。
考虑其他替代方案,例如服务器状态、URL 状态或本地存储。对于服务器数据,可以使用 React Query 等库来处理请求和缓存。对于 URL 状态,可以使用 React Router 的 useLocation
或 Next.js 的内置方法。
在使用 useState
之前,考虑以下几点:
- 这个值是否可以在渲染时简单派生?
- 有库已经管理这个状态了吗?
- 这个值是否需要触发重渲染?
如果这些问题的答案都是“否”,那么你可能不需要 useState
。
从已有状态中派生值
一个鲜为人知的技巧是:派生值不需要存储在状态中。如果你的数据可以从已有状态或属性中计算出来,直接在渲染时计算它们。
示例代码:
const formattedDate = new Date(date).toLocaleDateString();
以上代码在无需存储的情况下从给定的 date
输入派生出格式化的日期字符串。这种方法避免了不必要的状态管理,简化了渲染逻辑,并保持组件高效。
不要用 useEffect
来计算值
停止使用 useEffect
进行简单的计算!如果你的值可以直接从状态或属性计算出来且不涉及副作用,那就直接在渲染时计算。对于高耗时计算,可以用 useMemo
优化性能:
示例代码:
const expensiveValue = useMemo(() => computeExpensiveValue(data), [data]);
这一做法将使用 useMemo
钩子在 data
变化时重新计算 expensiveValue
,避免每次渲染时不必要的重新计算,提升了性能。
keys
应该是唯一的
许多人都有过这样的错误:在列表中使用数组索引作为 key
。但这是不好的做法,React 需要唯一的 key
来识别项目,使用非唯一值会导致各种问题。
你可以用 crypto.randomUUID()
生成唯一 ID,但务必要在状态更新时生成,而不是每次渲染时。对于对象,可以考虑添加一个 id
属性:
示例代码:
const itemWithId = items.map(item => ({ ...item, id: generateUniqueId() }));
这样每个项目都会拥有一个唯一的 key
,确保 React 能高效地管理更新和渲染列表。
不要遗漏依赖项
React 的一个常见陷阱是:在 useEffect
中遗漏依赖项会导致“过时闭包”(stale closures)。例如,如果你在 useEffect
中没有包含所有需要的依赖项,可能导致效果未按预期更新。
务必仔细检查你的依赖项数组:
示例代码:
useEffect(() => {
// Effect logic
}, [dependency]);
确保在依赖项数组中包含所有相关的依赖,以保证效果的正确运行,避免遗漏更新造成的潜在问题。
按需使用 useEffect
不要急着使用 useEffect
。虽然它很强大,但滥用会导致代码混乱。React 框架提供了更优雅的解决方案来管理副作用。对于数据获取,可以考虑使用 TanStack Query(前身是 React Query)或 SWR 这样的库,它们高效地处理请求和缓存,提升用户体验。
替代策略包括:
- 直接派生值
- 使用事件处理器响应事件
- 在服务器或专门的库中获取数据
示例:如何使用 TanStack Query
import { useQuery } from 'react-query';
const fetchUser = async () => {
const response = await fetch('https://api.example.com/user');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
function User() {
const { data, error, isLoading } = useQuery('user', fetchUser);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading user</div>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
);
}
这个示例展示了如何使用 react-query
来处理数据获取。在不使用复杂的 useEffect
和状态管理的情况下,直接通过 useQuery
管理服务器端状态,代码更为简洁和高效。
最后
这篇文章对 React 的一些最佳实践进行了详细阐述,通过多个真实例子帮助你更好地掌握这些技巧,并在实际开发中高效应用。希望这版中文文章能让你更轻松地理解和应用这些知识点。
如果今天的文章有帮到你,希望能够得到一个免费的点赞和关注~