阅读视图
React状态更新踩坑记:我是这样优雅修改参数的
大家好,我是小杨,一名有6年经验的前端开发工程师。在React开发中,状态(State)和参数(Props)的修改是最基础但也最容易踩坑的部分。今天我就来分享几种常见的React参数修改方法,以及我在项目中总结的最佳实践,避免大家走弯路。
1. 直接修改State?大忌!
新手常犯的一个错误是直接修改state
,比如:
// ❌ 错误示范:直接修改state
this.state.count = 10;
React的state
是不可变(Immutable) 的,直接修改不会触发重新渲染。正确的做法是使用setState
(类组件)或useState
的更新函数(函数组件)。
2. 类组件:setState的正确姿势
在类组件里,修改状态必须用setState
:
class Counter extends React.Component {
state = { count: 0 };
increment = () => {
// ✅ 正确方式:使用setState
this.setState({ count: this.state.count + 1 });
};
render() {
return <button onClick={this.increment}>Count: {this.state.count}</button>;
}
}
注意:setState
是异步的,如果依赖前一个状态,应该用函数式更新:
this.setState((prevState) => ({ count: prevState.count + 1 }));
3. 函数组件:useState + 不可变更新
在函数组件里,我们使用useState
,同样要遵循不可变原则:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
// ✅ 正确方式:使用useState的更新函数
setCount(count + 1);
};
return <button onClick={increment}>Count: {count}</button>;
}
如果新状态依赖旧状态,推荐使用函数式更新:
setCount((prevCount) => prevCount + 1);
4. 修改对象或数组:避免引用突变
React要求状态更新必须是不可变的,所以直接修改对象或数组的属性是不行的:
const [user, setUser] = useState({ name: 'Alice', age: 25 });
// ❌ 错误:直接修改对象
user.age = 26;
setUser(user); // 不会触发更新!
// ✅ 正确:创建新对象
setUser({ ...user, age: 26 });
数组的更新也要遵循不可变原则:
const [todos, setTodos] = useState(['Learn React', 'Write Blog']);
// ✅ 正确:使用展开运算符或map/filter
setTodos([...todos, 'New Task']); // 添加
setTodos(todos.filter((todo) => todo !== 'Learn React')); // 删除
5. 性能优化:useState vs useReducer
如果状态逻辑较复杂,useState
可能会变得臃肿,这时可以用useReducer
:
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error('Unknown action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</div>
);
}
useReducer
适合管理复杂状态逻辑,比如表单、全局状态等。
6. 常见坑点 & 解决方案
① 连续setState不会立即更新
// ❌ 连续调用setState,count只会+1
setCount(count + 1);
setCount(count + 1);
// ✅ 使用函数式更新
setCount(prev => prev + 1);
setCount(prev => prev + 1); // 现在会+2
② useEffect依赖问题
如果useEffect
依赖state
,但忘记加进依赖数组,可能导致闭包问题:
useEffect(() => {
console.log(count); // 可能拿到旧值
}, []); // ❌ 缺少依赖
useEffect(() => {
console.log(count); // ✅ 正确
}, [count]); // 依赖正确
总结
- 不要直接修改
state
,使用setState
或useState
的更新函数 - 对象/数组更新时,创建新引用
- 复杂状态逻辑用
useReducer
- 连续更新用函数式
setState
useEffect
依赖要写全
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!
告别Class组件!用useEffect玩转React生命周期
React状态管理:从Context到Redux,我的选型心得
React性能优化神器useMemo!这样用才不浪费,新手必看指南
大家好,我是小杨,一个在React坑里摸爬滚打6年的前端老鸟。今天要跟大家聊聊useMemo
这个性能优化利器——用好了能让你的应用飞起来,用错了反而会让代码更难维护!
真实案例:我的性能翻车现场
去年我开发一个数据分析看板时,遇到了严重的卡顿问题:
function AnalyticsDashboard({ data }) {
// 复杂的计算函数
const calculateStats = () => {
console.log("正在重新计算..."); // 这里疯狂打印
// 假设这里是非常耗时的计算
return {
avg: data.reduce((a, b) => a + b.value, 0) / data.length,
max: Math.max(...data.map(item => item.value)),
min: Math.min(...data.map(item => item.value))
};
};
const stats = calculateStats(); // ❌ 每次渲染都重新计算
return (
<div>
<h3>我的数据分析看板</h3>
<p>平均值: {stats.avg}</p>
{/* 其他渲染... */}
</div>
);
}
当我在这个看板上添加筛选功能后,每次筛选都会导致页面卡顿1-2秒,用户体验极差!
useMemo的正确打开方式
function AnalyticsDashboard({ data }) {
const stats = useMemo(() => {
console.log("只有data变化时才重新计算");
return {
avg: data.reduce((a, b) => a + b.value, 0) / data.length,
max: Math.max(...data.map(item => item.value)),
min: Math.min(...data.map(item => item.value))
};
}, [data]); // 👈 关键依赖项
// ...其余代码不变
}
效果立竿见影:
- 只有
data
变化时才重新计算 - 其他状态变化(比如筛选器UI状态)不会触发计算
- 性能提升300%!
什么时候该用useMemo?
我总结了一个简单判断标准:
✅ 该用的情况:
- 计算成本高的操作(大数据量转换、复杂数学运算)
- 作为其他Hook的依赖项时(避免不必要的effect触发)
- 传递给子组件的引用类型数据(对象/数组)
❌ 不该滥用的情况:
- 简单的基本类型计算(a+b这种)
- 组件本身已经很轻量时
- 依赖项频繁变化的情况
进阶技巧:配合其他Hook使用
function UserProfile({ userId }) {
const user = useMemo(() => ({ id: userId }), [userId]);
useEffect(() => {
// 只有当userId变化时才执行
fetchUserDetails(user);
}, [user]); // 👈 依赖memoized的对象
// ...
}
常见误区避坑指南
-
依赖项漏写:会导致缓存不更新
// ❌ 错误示例 useMemo(() => {...}, []); // 空依赖
-
过度优化:反而增加内存开销
// ❌ 没必要 const name = useMemo(() => '小明', []);
-
错误期待:不能阻止子组件渲染,要和React.memo配合使用
性能对比实测
在我的MacBook Pro上测试(10000条数据):
方案 | 计算耗时 | 重新渲染次数 |
---|---|---|
不用useMemo | 120ms | 每次渲染 |
使用useMemo | 120ms | 仅数据变化时 |
差异 | 相同 | 减少90%+ |
我的实战建议
- 先写功能再优化:不要一开始就用useMemo
- 用Chrome DevTools检测:通过Performance面板找出真正瓶颈
- 团队统一规范:我们团队约定超过100条数据的计算必须用useMemo
记得有个同事在代码评审时问我:"为什么这里要用useMemo?",当我展示优化前后的性能对比后,他立刻给PR点了赞!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!
React父组件props变了,子组件如何立刻知道?3种监听方案实测!
大家好,我是小杨,一个和React相爱相杀6年的前端开发者。今天要解决一个经典问题:当父组件的props更新时,子组件如何及时响应? 这个问题我面试新人时经常问,实际开发中也踩过不少坑。
场景还原:一个血泪教训
上周我重构一个商品详情页时遇到这个问题:
// 父组件
function ProductPage({ productId }) {
const [productData, setProductData] = useState({});
useEffect(() => {
fetch(`/api/products/${productId}`)
.then(res => res.json())
.then(data => setProductData(data));
}, [productId]);
return <ProductDetail data={productData} />;
}
// 子组件
function ProductDetail({ data }) {
// ❌ 问题:当productId变化时,这里没有立即响应!
return <div>{data.name}</div>;
}
当用户切换商品时,子组件竟然还显示上一个商品的信息!经过排查,我发现React的渲染机制需要特殊处理。下面分享3种解决方案:
方案1:useEffect监听(推荐🔥)
function ProductDetail({ data }) {
useEffect(() => {
// 这里可以执行数据更新后的操作
console.log('收到新数据:', data);
// 比如重置子组件内部状态
}, [data]); // 👈 关键依赖项
return <div>{data.name}</div>;
}
优点:
- 代码简洁直观
- 完美契合Hook体系
坑点:
- 注意不要漏写依赖项,否则可能不生效
方案2:key属性暴力重置(简单粗暴)
<ProductDetail
data={productData}
key={productId} // 👈 关键点
/>
原理:
当key变化时,React会直接销毁旧组件实例,创建新实例
适用场景:
- 需要完全重置子组件内部状态时
- 组件初始化成本不高的情况
方案3:使用useMemo优化渲染
function ProductPage({ productId }) {
const [productData, setProductData] = useState({});
// ...获取数据逻辑...
const memoizedChild = useMemo(() => (
<ProductDetail data={productData} />
), [productData]); // 👈 只有data变化时才重新渲染
return memoizedChild;
}
适用场景:
- 子组件渲染成本很高时
- 需要精细控制渲染时机
性能对比小贴士
方案 | 重新渲染时机 | 适用场景 |
---|---|---|
useEffect | props变化后执行副作用 | 需要响应式操作 |
key | 完全重建组件 | 需要彻底重置 |
useMemo | 依赖项变化时 | 优化高性能组件 |
我的实战建议
- 优先用useEffect:符合React设计哲学,90%场景够用
- 表单场景用key:比如切换不同用户的编辑表单时
- 性能敏感用useMemo:比如大数据量表格组件
记得去年我做电商后台时,用key
方案解决了商品编辑表单的状态残留问题,PM直呼神奇!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!
useState vs setState:React状态管理,你站哪一队?
大家好,我是小杨,一个摸爬滚打了6年的前端老司机。今天咱们来聊聊React里两个最常用的状态管理方式——setState
和useState
。虽然它们干的事儿差不多,但用起来可是有讲究的!
一、先说说setState
(Class组件版)
以前用Class组件写React的时候,setState
是我的好搭档。它有个特点: "异步更新" 。
举个🌰,我想记录点击按钮的次数:
class Counter extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 🤦♂️ 这里打印的还是旧值!
};
render() {
return <button onClick={this.handleClick}>我点了 {this.state.count} 次</button>;
}
}
坑点注意:
-
setState
是"批量更新"的,连续调用多次可能会被合并 -
想拿到最新值?用回调函数形式:
this.setState(prevState => ({ count: prevState.count + 1 }), () => { console.log('现在是最新值:', this.state.count); });
二、再聊聊useState
(函数组件Hook版)
自从Hook横空出世,函数组件也能有状态了!但useState
的玩法有点不一样:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 😅 依然打印旧值!
};
return <button onClick={handleClick}>我点了 {count} 次</button>;
}
关键区别:
-
更新不会自动合并(需要用扩展运算符手动合并对象)
-
想拿到最新值?用函数式更新:
setCount(prevCount => prevCount + 1);
三、终极对比表格
特性 | setState (Class) | useState (Hook) |
---|---|---|
更新方式 | 自动合并对象 | 需要手动合并 |
获取最新值 | 用回调函数 | 用函数式更新 |
异步表现 | 批量更新 | 闭包问题(需要留意) |
代码量 | 啰嗦 | 简洁 |
四、我的实战建议
- 新项目直接用Hook:代码更简洁,未来趋势
- 老项目迁移别强求:Class组件还能再战500年
- 闭包问题要小心:在useEffect里用状态时,记得加依赖项
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!