还记得刚学React时,我被类组件的生命周期搞得头大------componentDidMount
、componentDidUpdate
、componentWillUnmount
... 这么多方法要记!直到Hooks出现,特别是useEffect
,让我终于能用一个API搞定所有场景。今天就来分享我的useEffect
实战心得,帮你彻底告别生命周期方法的烦恼!
🎯 一、为什么说useEffect是"生命周期替代品"?
以前在类组件中,我们需要在不同的生命周期方法中编写代码:
jsx
class OldComponent extends React.Component {
componentDidMount() {
console.log('组件挂载了');
this.timer = setInterval(() => {
this.doSomething();
}, 1000);
}
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
console.log('props变化了');
}
}
componentWillUnmount() {
console.log('组件要卸载了');
clearInterval(this.timer);
}
render() {
return <div>老式写法</div>;
}
}
现在用useEffect
,一个API搞定所有:
jsx
const NewComponent = ({ value }) => {
useEffect(() => {
console.log('组件挂载了');
const timer = setInterval(() => {
doSomething();
}, 1000);
return () => {
console.log('组件要卸载了');
clearInterval(timer);
};
}, []);
useEffect(() => {
console.log('value变化了:', value);
}, [value]);
return <div>新时代写法</div>;
};
是不是清爽多了?
🛠️ 二、useEffect的三种使用姿势
1. 模拟componentDidMount:只运行一次
jsx
const UserProfile = () => {
const [userData, setUserData] = useState(null);
useEffect(() => {
// 组件挂载时获取用户数据
const fetchUserData = async () => {
try {
const response = await fetch('/api/user');
const data = await response.json();
setUserData(data);
} catch (error) {
console.error('获取数据失败:', error);
}
};
fetchUserData();
}, []); // 空依赖数组 = 只运行一次
return <div>{userData?.name || '加载中...'}</div>;
};
💡 关键 :空数组[]
作为第二个参数
2. 模拟componentDidUpdate:依赖变化时运行
jsx
const SearchResults = ({ query }) => {
const [results, setResults] = useState([]);
useEffect(() => {
// 搜索词变化时重新搜索
if (query) {
const search = async () => {
const newResults = await fetchResults(query);
setResults(newResults);
};
search();
}
}, [query]); // query变化时重新执行
return (
<div>
{results.map(item => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
};
🚨 陷阱提醒:忘记加依赖数组会导致每次渲染都执行!
3. 模拟componentWillUnmount:清理副作用
jsx
const RealTimeData = () => {
useEffect(() => {
// 建立WebSocket连接
const ws = new WebSocket('ws://api.example.com');
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
};
// 返回清理函数
return () => {
ws.close();
console.log('WebSocket连接已关闭');
};
}, []);
return <div>实时数据组件</div>;
};
🎯 重点:return的函数会在组件卸载时自动执行
⚠️ 三、我踩过的坑和解决方案
问题1:无限循环地狱
jsx
// ❌ 错误写法:缺少依赖
useEffect(() => {
setCount(count + 1); // 会导致无限重新渲染!
});
// ✅ 正确写法1:使用函数式更新
useEffect(() => {
setCount(prevCount => prevCount + 1);
}, []);
// ✅ 正确写法2:添加完整依赖
useEffect(() => {
// 一些操作
}, [count]); // 明确声明依赖
问题2:异步操作竞争条件
jsx
useEffect(() => {
let isActive = true;
const fetchData = async () => {
const result = await fetchSomeData(id);
// 防止组件已卸载时设置状态
if (isActive) {
setData(result);
}
};
fetchData();
return () => {
isActive = false;
};
}, [id]);
🎯 四、实用技巧:自定义Hook封装
我把常用的useEffect模式封装成了自定义Hook:
jsx
// 封装数据获取逻辑
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
// 使用示例
const UserList = () => {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <div>加载中...</div>;
if (error) return <div>出错啦: {error}</div>;
return (
<div>
{data.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
};
📝 五、总结:useEffect最佳实践
- 明确依赖:认真填写依赖数组,避免无限循环
- 及时清理:返回清理函数,防止内存泄漏
- 分离关注点:多个不相关的副作用用多个useEffect
- 自定义Hook:复用逻辑,保持组件简洁
从生命周期方法到useEffect,不仅是API的变化,更是编程思维的进化。刚开始可能需要适应,但一旦掌握,你就会发现函数组件的简洁和强大。
💪 动手试试吧:下次写组件时,尝试用useEffect重构旧代码,你会发现代码变得更清晰易维护!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!