React中为何要使用useEffect或componentDidMount?多组实现代码差异对比
React 组件数据请求写法差异对比
函数组件两组写法差异
写法1:直接在函数体调用请求
const Product_List = (props) => { const [data, setData] = useState(); getData((response)=> setData(response)); return( <FlatList data={data} /> ) }
存在的问题:
- React函数组件每次渲染都会完整执行整个函数体,只要组件触发重渲染(比如props变化、state更新、父组件重渲染),都会重新调用一次
getData发起新请求 - 最常见的问题是触发无限请求死循环:
getData回调执行setData会触发组件重渲染,重渲染又会调用getData,无限往复,严重占用资源 - 请求逻辑完全不受控,无法做到只在指定时机触发请求
写法2:空依赖useEffect包裹请求
const Product_List = (props) => { const [data, setData] = useState(); useEffect(() => { getData((response)=> setData(response)); }, []); return( <FlatList data={data} /> ) }
特性:
- 空依赖数组的
useEffect只会在组件首次挂载完成后执行1次,后续无论组件怎么重渲染都不会重复执行 - 不会出现无限请求的问题,请求逻辑可控,是React官方推荐的组件挂载时拉取数据的标准写法
- 可以直接在useEffect里返回清理函数,处理组件卸载时中断请求、清理副作用的需求,避免内存泄漏
类组件两组写法差异
写法1:constructor中直接调用请求
class Product_List extends Component{ constructor(props) { this.state = { data: [] } getData((response)=> this.setState({data: response})); } render(){ return( <FlatList data={data} /> ) } }
存在的问题:
- constructor的作用仅为初始化state、绑定实例方法,不应该承载副作用逻辑
- constructor执行时组件还未完成挂载,若请求返回速度快于组件挂载速度,此时调用
setState虽然不会报错,但不符合生命周期执行逻辑 - 服务端渲染场景下,constructor会在服务端执行,会导致API请求在服务端就被触发,不符合客户端拉取数据的预期
- 若组件实例化后还没完成挂载就被销毁,请求回调触发
setState会抛出内存泄漏警告
写法2:componentDidMount中调用请求
class Product_List extends Component{ constructor(props) { this.state = { data: [] } } componentDidMount(){ getData((response)=> this.setState({data: response})); } render(){ return( <FlatList data={data} /> ) } }
特性:
componentDidMount是组件完成挂载到DOM后才会触发的生命周期钩子,只会执行1次,是类组件官方推荐的挂载后拉取数据的写法- 服务端渲染场景下
componentDidMount不会在服务端执行,符合客户端发起请求的预期 - 可以配合
componentWillUnmount生命周期实现请求中断、副作用清理逻辑,避免内存泄漏
内容的提问来源于stack exchange,提问作者Sajad Speed
相关产品推荐
相关产品推荐

