前端状态管理吐槽:别再把你的状态搞得像乱麻!

前端状态管理吐槽:别再把你的状态搞得像乱麻!

毒舌时刻

前端状态管理就像谈恋爱——刚开始觉得很美好,时间久了就会发现一堆问题。Redux、MobX、Zustand、Pinia... 一堆状态管理库让你挑花了眼,结果你的状态还是乱得像一锅粥。

我就想不明白了,为什么管理个状态要这么复杂?你看看现在的代码,action、reducer、middleware,一堆概念把人搞晕。还有那些用Context API的,状态嵌套得比俄罗斯套娃还深,你说这能不难维护吗?

别跟我提什么"状态管理最佳实践",先把你的状态结构捋明白了再说。还有那些滥用全局状态的,什么都往全局状态里放,结果状态变得比你家的杂物间还乱。

为什么你需要这个

  1. 代码可维护性:良好的状态管理能让你的代码更清晰,更容易维护。
  2. 状态一致性:避免状态不一致导致的bug,让你的应用更稳定。
  3. 调试方便:好的状态管理库提供了调试工具,让你更容易定位问题。
  4. 面试必备:面试官最喜欢问状态管理的问题,掌握这些能让你面试更有底气。
  5. 装X神器:跟同事聊起来,你能说上几句状态管理的优化技巧,瞬间提升逼格。

反面教材

// 1. 滥用全局状态 // store.js import { createStore } from 'redux'; const initialState = { user: null, posts: [], comments: [], likes: [], notifications: [], settings: {}, // 一堆乱七八糟的状态 }; function rootReducer(state = initialState, action) { switch (action.type) { case 'SET_USER': return { ...state, user: action.payload }; case 'SET_POSTS': return { ...state, posts: action.payload }; case 'SET_COMMENTS': return { ...state, comments: action.payload }; case 'SET_LIKES': return { ...state, likes: action.payload }; case 'SET_NOTIFICATIONS': return { ...state, notifications: action.payload }; case 'SET_SETTINGS': return { ...state, settings: action.payload }; // 一堆case语句 default: return state; } } const store = createStore(rootReducer); // 问题:状态过于庞大,难以管理 // 2. 嵌套过深的Context import React, { createContext, useContext, useState } from 'react'; const UserContext = createContext(); const PostsContext = createContext(); const CommentsContext = createContext(); function App() { const [user, setUser] = useState(null); const [posts, setPosts] = useState([]); const [comments, setComments] = useState([]); return ( <UserContext.Provider value={{ user, setUser }}> <PostsContext.Provider value={{ posts, setPosts }}> <CommentsContext.Provider value={{ comments, setComments }}> <Main /> </CommentsContext.Provider> </PostsContext.Provider> </UserContext.Provider> ); } // 问题:Context嵌套过深,代码难以阅读 // 3. 不规范的状态更新 function BadComponent() { const [state, setState] = useState({ user: { name: 'John', age: 30, address: { street: '123 Main St', city: 'New York' } } }); // 直接修改状态,React不会检测到变化 const handleUpdate = () => { state.user.name = 'Jane'; setState(state); }; return ( <div> <h1>{state.user.name}</h1> <button onClick={handleUpdate}>Update Name</button> </div> ); } // 问题:直接修改状态,React不会重新渲染 // 4. 过度使用状态管理库 // 对于简单的应用,使用Redux是杀鸡用牛刀 import { createStore } from 'redux'; const initialState = { count: 0 }; function counterReducer(state = initialState, action) { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 }; case 'DECREMENT': return { ...state, count: state.count - 1 }; default: return state; } } const store = createStore(counterReducer); // 问题:对于简单的计数应用,使用Redux过于复杂 

问题

  • 滥用全局状态,状态过于庞大
  • Context嵌套过深,代码难以阅读
  • 不规范的状态更新,React不会检测到变化
  • 过度使用状态管理库,杀鸡用牛刀

正确的做法

前端状态管理指南

// 1. 合理使用状态管理库 // 对于复杂应用,使用Redux Toolkit import { configureStore, createSlice } from '@reduxjs/toolkit'; // 按功能分割状态 const userSlice = createSlice({ name: 'user', initialState: null, reducers: { setUser: (state, action) => action.payload, clearUser: () => null } }); const postsSlice = createSlice({ name: 'posts', initialState: [], reducers: { setPosts: (state, action) => action.payload, addPost: (state, action) => [...state, action.payload] } }); const store = configureStore({ reducer: { user: userSlice.reducer, posts: postsSlice.reducer } }); // 2. 合理使用Context API import React, { createContext, useContext, useState, useMemo } from 'react'; // 创建一个包含所有状态的Context const AppContext = createContext(); export function useAppContext() { const context = useContext(AppContext); if (!context) { throw new Error('useAppContext must be used within AppProvider'); } return context; } export function AppProvider({ children }) { const [user, setUser] = useState(null); const [posts, setPosts] = useState([]); const [comments, setComments] = useState([]); // 使用useMemo缓存值,避免不必要的渲染 const value = useMemo(() => ({ user, setUser, posts, setPosts, comments, setComments }), [user, posts, comments]); return ( <AppContext.Provider value={value}> {children} </AppContext.Provider> ); } // 3. 规范的状态更新 function GoodComponent() { const [state, setState] = useState({ user: { name: 'John', age: 30, address: { street: '123 Main St', city: 'New York' } } }); // 正确的状态更新方式 const handleUpdate = () => { setState(prevState => ({ ...prevState, user: { ...prevState.user, name: 'Jane', address: { ...prevState.user.address, city: 'Los Angeles' } } })); }; return ( <div> <h1>{state.user.name}</h1> <p>{state.user.address.city}</p> <button onClick={handleUpdate}>Update</button> </div> ); } // 4. 选择合适的状态管理方案 // 简单应用:使用useState function SimpleComponent() { const [count, setCount] = useState(0); return ( <div> <h1>Count: {count}</h1> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } // 中等复杂度应用:使用Context API // 复杂应用:使用Redux Toolkit或Zustand // 5. 使用Zustand简化状态管理 import create from 'zustand'; const useStore = create((set) => ({ user: null, posts: [], setUser: (user) => set({ user }), setPosts: (posts) => set({ posts }), addPost: (post) => set((state) => ({ posts: [...state.posts, post })) })); function Component() { const { user, posts, setUser, addPost } = useStore(); return ( <div> {user && <h1>Hello, {user.name}</h1>} <button onClick={() => addPost({ id: Date.now(), title: 'New Post' })}> Add Post </button> <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> ); } 

状态管理最佳实践

  1. 状态分层
    • 全局状态:用户信息、认证状态等
    • 局部状态:组件内部状态
    • URL状态:路由参数等
  2. 状态规范化
    • 使用扁平的状态结构
    • 避免深层嵌套
    • 使用ID作为键,便于查找和更新
  3. 副作用处理
    • 使用Redux Thunk或Saga处理异步操作
    • 使用React Query或SWR处理数据获取
  4. 性能优化
    • 使用useMemo和useCallback缓存计算和函数
    • 使用React.memo避免不必要的渲染
    • 合理使用状态管理库的选择器
  5. 调试工具
    • 使用Redux DevTools
    • 使用Zustand的devtools中间件
    • 定期检查状态变化

毒舌点评

前端状态管理就像整理房间——刚开始觉得很麻烦,但整理好了就会很舒服。但很多开发者就是懒,不愿意花时间去整理状态,结果状态变得乱七八糟。

我就想问一句:你写的代码是给人看的还是给机器看的?如果你的状态管理混乱不堪,别人怎么理解你的代码?

还有那些用Redux的,写了一堆action、reducer,结果状态还是管理不好。你就不能用Redux Toolkit吗?它都帮你处理了很多 boilerplate 代码。

还有那些用Context API的,嵌套得比俄罗斯套娃还深。你就不能把相关的状态放在一个Context里吗?

还有那些直接修改状态的,React不会检测到变化,你知道吗?你就不能用函数式更新吗?

还有那些过度使用状态管理库的,一个简单的计数应用也要用Redux,你是不是闲得慌?

作为一名前端手艺人,我想对所有开发者说:

别再把你的状态搞得像乱麻了!花点时间整理一下,你的代码会变得更清晰,更容易维护。

记住,状态管理不是目的,而是手段。它是为了让你的应用更稳定,更易于维护,不是为了增加代码复杂度。

最后,我想说:选择合适的状态管理方案,根据应用的复杂度来决定。不要盲目跟风,适合自己的才是最好的。

所以,从今天开始,整理你的状态吧!让你的代码变得更清晰,让你的应用变得更稳定。

Read more

使用 Trae IDE 一键将 Figma 转为前端代码

在现代前端开发中,从设计稿到可用页面的交付往往需要大量重复劳动:切图、手写样式、布局调整……而借助 MCP Server - Figma AI Bridge,我们可以将 Figma 设计稿自动转换成整洁的 HTML/CSS/JS 代码,并立即生成可预览的网页。一键化、傻瓜式操作,让设计交付效率跃升。 本文测试使用的系统环境如下: * Trae IDE 版本:2.4.5 * macOS 版本:14.7 * Node.js 版本:24.6.0 * npx 版本:11.5.2 * Python 版本:3.13.3

通过URI Scheme实现从Web网页上打开本地C++应用程序(以腾讯会议为例,附完整实现源码)

通过URI Scheme实现从Web网页上打开本地C++应用程序(以腾讯会议为例,附完整实现源码)

目录 1、需求描述 2、选择URI Scheme实现 3、何为URI Scheme? 4、将自定义的URL Scheme信息写入注册表的C++源码实现 5、如何实现最开始的3种需求 6、后续需要考虑的细节问题        之前陆续收到一些从Web页面上启动我们C++客户端软件的需求,希望我们能提供一些技术上的支持与协助,支持从Web网页上将我们的C++客户端软件启动起来。于是我大概地研究了相关的实现方法,下面把研究的过程与结果在此做一个分享,希望能给大家提供一个借鉴或参考。 C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达10000多个,欢迎订阅,持续更新...)https://blog.ZEEKLOG.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新500多篇,订阅量已达8000多个,欢迎订阅,持续更新中...)https://blog.ZEEKLOG.net/

WebDAV服务器快速部署指南:简单高效的文件共享解决方案

WebDAV服务器快速部署指南:简单高效的文件共享解决方案 【免费下载链接】webdavSimple Go WebDAV server. 项目地址: https://gitcode.com/gh_mirrors/we/webdav 想要在多设备间轻松同步文件?需要一个安全可靠的文件共享平台?WebDAV服务器正是你寻找的完美解决方案。这个基于Go语言开发的WebDAV部署工具,让你能够快速搭建个人文件服务器,实现远程文件管理和访问。 🚀 极速启动:三种简单安装方式 一键安装方法让WebDAV部署变得异常简单: * Homebrew安装:只需在终端输入brew install webdav * Go工具安装:使用命令go install github.com/hacdias/webdav/v5@latest * 源码构建:克隆仓库后执行go build命令 Docker快速部署更是简单到极致: docker run -p 6060:6060 -v $(pwd)/data:/data

前端老哥必看:input光标处插入字符串的骚操作(附完整代码)

前端老哥必看:input光标处插入字符串的骚操作(附完整代码)

前端老哥必看:input光标处插入字符串的骚操作(附完整代码) * 前端老哥必看:input光标处插入字符串的骚操作(附完整代码) * 开篇先吐槽这破需求 * 先说结论:selectionStart和selectionEnd是你最好的朋友 * 核心代码其实就那几行 * 不同浏览器给你整的幺蛾子 * 实际项目里这些场景逃不掉 * 踩坑之后的血泪排查经验 * 让代码更优雅的几条野路子 * 最后说点掏心窝子的话 前端老哥必看:input光标处插入字符串的骚操作(附完整代码) 开篇先吐槽这破需求 说实话,我到现在还记得那天下午产品经理晃悠到我工位旁边,一脸轻松地说:“哥,咱们这个输入框啊,得支持在光标位置插入文本,就那种@人的功能,你懂的吧?” 我当时嘴里叼着半根辣条,差点没噎死。 啥?光标位置插入?我脑子里第一反应是:这玩意儿不是挺简单的吗,不就一个input.value += 'xxx'的事?然后产品经理补了一句:“要在当前光标位置插入哦,不能跑到最后面去。” 我当场就愣住了。 辣条也不嚼了,脑子里开始疯狂检索