前端开发 + 数据结构和算法:DSA 如何为您的 React 应用提供支持
专注于前端的面试通常根本不关心 DSA。
对于我们这些记得在学校/大学学习过 DSA 的人来说,所有的例子都感觉纯粹是算法(有充分的理由),但几乎没有任何例子或指导关于我们每天使用的产品如何利用这个概念。
“我会需要这个吗?”
你经常问这个问题,对吧?👀
以下是一些您现在可以在 React 应用中利用的数据结构!👇
目录
- 介绍
- 数组:状态管理的首选
- 对象和哈希映射:提高效率的规范化数据存储
- 双向链表:上下文导航
- 堆栈:具有不可变行为的撤消/重做功能
- 队列:管理顺序 API 调用
- 树:渲染递归组件
- 图表:构建复杂的数据关系和导航
- 结论
相关阅读:
1. 数组🧩:状态管理的首选
React 中数组无处不在。如果你需要帮助理解它们的工作原理.map()
,.filter()
那么这篇文章可能有点太早了!不过不用担心——一旦你熟悉了这些数组方法,你就会发现它们对于渲染列表、管理组件状态和转换数据有多么重要。
2. 对象和哈希映射🗺️:规范化的数据存储以提高效率
在 React 应用中,当你处理大量实体(例如用户或帖子)时,将数据规范化为对象(哈希映射)可以提高读取和更新的效率。你无需处理深层嵌套的结构,而是通过实体的 ID 进行映射。
示例:从具有 ID 的规范化存储中读取
const postsById = {
1: { id: 1, title: 'First Post', content: 'Content of first post' },
2: { id: 2, title: 'Second Post', content: 'Content of second post' }
};
const postIds = [1, 2];
function PostList() {
return (
<div>
{postIds.map(id => (
<Post key={id} post={postsById[id]} />
))}
</div>
);
}
function Post({ post }) {
return (
<div>
<h2>{post.title}</h2>
<p>{post.content}</p>
</div>
);
}
这种模式允许高效的数据访问,特别是对于需要快速进行更新或读取而无需重新呈现整个集合的大型数据集。
3. 双向链表🔗:使用上下文导航
当你需要同时获取前一个和下一个元素的上下文信息时,双向链表非常有用——想象一下浏览一个图片库,其中每张图片都会显示其相邻的图片以供参考。我们不使用索引,而是将当前节点直接存储在组件状态中。
示例:用于在具有上下文的元素之间导航的双向链接列表
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
}
add(value) {
const newNode = new Node(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
}
}
const imageList = new DoublyLinkedList();
imageList.add({ id: 1, src: 'image1.jpg', alt: 'First Image' });
imageList.add({ id: 2, src: 'image2.jpg', alt: 'Second Image' });
imageList.add({ id: 3, src: 'image3.jpg', alt: 'Third Image' });
function Gallery() {
const [currentNode, setCurrentNode] = useState(imageList.head);
return (
<div>
{currentNode.prev && (
<img src={currentNode.prev.value.src} alt={currentNode.prev.value.alt} className="prev-image" />
)}
<img src={currentNode.value.src} alt={currentNode.value.alt} className="main-image" />
{currentNode.next && (
<img src={currentNode.next.value.src} alt={currentNode.next.value.alt} className="next-image" />
)}
<div>
<button onClick={() => setCurrentNode(currentNode.prev)} disabled={!currentNode.prev}>
Previous
</button>
<button onClick={() => setCurrentNode(currentNode.next)} disabled={!currentNode.next}>
Next
</button>
</div>
</div>
);
}
在这个 React 组件中:
- 当前节点存储在状态中,并且 UI 根据是否存在前一个或下一个节点进行更新。
- 这些按钮使用户能够向前和向后导航列表,如果没有更多节点可移动,则禁用。
- 这种结构利用周围元素的上下文模拟实时导航,通常用于轮播、媒体库或播放列表等 UI 组件。
4. Stacks🚀:具有不可变行为的撤消/重做功能
堆栈允许您使用后进先出(LIFO) 逻辑高效地管理撤消/重做操作。通过使用不可变操作 ( , ),我们可以确保状态保持不变。concat
slice
示例:使用不可变的撤消/push
重做pop
const [undoStack, setUndoStack] = useState([]);
const [redoStack, setRedoStack] = useState([]);
const [formState, setFormState] = useState({ name: '', email: '' });
const updateForm = (newState) => {
setUndoStack(prev => prev.concat([formState])); // Immutable push
setRedoStack([]); // Clear redo stack
setFormState(newState);
};
const undo = () => {
if (undoStack.length > 0) {
const lastState = undoStack.at(-1);
setUndoStack(prev => prev.slice(0, -1)); // Immutable pop
setRedoStack(prev => prev.concat([formState])); // Move current state to redo
setFormState(lastState);
}
};
const redo = () => {
if (redoStack.length > 0) {
const lastRedo = redoStack.at(-1);
setRedoStack(prev => prev.slice(0, -1)); // Immutable pop
setUndoStack(prev => prev.concat([formState])); // Push current state to undo
setFormState(lastRedo);
}
};
5. 队列📬:管理顺序 API 调用
队列以先进先出(FIFO) 的方式运行,非常适合确保 API 调用或通知等任务按正确的顺序处理。
示例:排队 API 调用
const [apiQueue, setApiQueue] = useState([]);
const enqueueApiCall = (apiCall) => {
setApiQueue(prevQueue => prevQueue.concat([apiCall])); // Immutable push
};
const processQueue = () => {
if (apiQueue.length > 0) {
const [nextCall, ...restQueue] = apiQueue;
nextCall().finally(() => setApiQueue(restQueue)); // Immutable pop
}
};
6. 树🌳:渲染递归组件
在处理嵌套组件(如评论线程、文件夹结构或菜单)时,React 中通常使用树。
示例:递归渲染评论树
const commentTree = {
id: 1,
text: "First comment",
children: [
{ id: 2, text: "Reply to first comment", children: [] },
{ id: 3, text: "Another reply", children: [{ id: 4, text: "Nested reply" }] }
]
};
function Comment({ comment }) {
return (
<div>
<p>{comment.text}</p>
{comment.children?.map(child => (
<div style={{ paddingLeft: '20px' }} key={child.id}>
<Comment comment={child} />
</div>
))}
</div>
);
}
另一篇可能与您相关的热门帖子:
7. 图表🎯:构建复杂的数据关系和导航
示例 1:多个视图之间的路由
您可以将页面之间的路由表示为图形,确保 SPA 中的导航路径灵活。
const routesGraph = {
home: ['about', 'contact'],
about: ['home', 'team'],
contact: ['home'],
};
function navigate(currentRoute, targetRoute) {
if (routesGraph[currentRoute].includes(targetRoute)) {
console.log(`Navigating from ${currentRoute} to ${targetRoute}`);
} else {
console.log(`Invalid route from ${currentRoute} to ${targetRoute}`);
}
}
示例 2:用户关系建模
图表非常适合建模社交联系或任何多个实体相互连接的关系。
const usersGraph = {
user1: ['user2', 'user3'],
user2: ['user1', 'user4'],
user3: ['user1'],
user4: ['user2']
};
function findConnections(userId) {
return usersGraph[userId] || [];
}
console.log(findConnections('user1')); // Outputs: ['user2', 'user3']
注意:我们使用图表来显示中间件中的审阅者依赖关系。
TL;DR — 学校课程的回报
这些 DSA 类在过去可能感觉很抽象,但数据结构在 React 中为你周围的世界提供动力。
对象、堆栈、队列、链表、树和图不仅仅是理论——它们是您每天构建的干净、高效和可扩展应用程序的支柱。
因此,下次您在队列中管理状态或处理复杂的 UI 逻辑时,请记住:您从学校开始就一直在为此进行训练。💪
让我知道您最常使用哪些数据结构!
文章来源:https://dev.to/jayantbh/frontend-dev-data-structs-algorithms-how-dsa-can-power-your-react-app-491a