从生命周期到useEffect:我的React函数组件进化之旅

还记得刚学React时,我被类组件的生命周期搞得头大------componentDidMountcomponentDidUpdatecomponentWillUnmount... 这么多方法要记!直到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最佳实践

  1. 明确依赖:认真填写依赖数组,避免无限循环
  2. 及时清理:返回清理函数,防止内存泄漏
  3. 分离关注点:多个不相关的副作用用多个useEffect
  4. 自定义Hook:复用逻辑,保持组件简洁

从生命周期方法到useEffect,不仅是API的变化,更是编程思维的进化。刚开始可能需要适应,但一旦掌握,你就会发现函数组件的简洁和强大。

💪 动手试试吧:下次写组件时,尝试用useEffect重构旧代码,你会发现代码变得更清晰易维护!

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
没有故事、有酒8 分钟前
Ajax介绍
前端·ajax·okhttp
朝新_12 分钟前
【SpringMVC】详解用户登录前后端交互流程:AJAX 异步通信与 Session 机制实战
前端·笔记·spring·ajax·交互·javaee
裴嘉靖14 分钟前
Vue 生成 PDF 完整教程
前端·vue.js·pdf
毕设小屋vx ylw28242616 分钟前
Java开发、Java Web应用、前端技术及Vue项目
java·前端·vue.js
冴羽1 小时前
今日苹果 App Store 前端源码泄露,赶紧 fork 一份看看
前端·javascript·typescript
蒜香拿铁1 小时前
Angular【router路由】
前端·javascript·angular.js
brzhang2 小时前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构
西洼工作室2 小时前
高效管理搜索历史:Vue持久化实践
前端·javascript·vue.js
广州华水科技2 小时前
北斗形变监测传感器在水库安全中的应用及技术优势分析
前端
樱花开了几轉2 小时前
element ui下拉框踩坑
开发语言·javascript·ui