React非主组件中如何用useEffect实现首次渲染仅执行一次数据渲染
问题场景
通过Axios拉取数据,需要实现组件首次渲染时仅在控制台打印一次数据,在非主组件中使用useEffect时触发报错。
- 报错截图:

- 次级组件代码截图:

- 主组件代码截图:

问题根因
这类报错基本是两个原因导致的:
- 违反React Hooks调用规则:
useEffect没有写在函数组件的顶层作用域,而是被放在了条件判断、循环、嵌套函数内部,或者直接写在了组件函数外面,导致Hooks执行顺序和React预期不一致。 - React 18严格模式默认开启:开发环境下组件首次挂载时会执行「挂载→卸载→重新挂载」的流程,对应
useEffect会执行两次,如果你的副作用逻辑没写清理函数、或者依赖数组配置错误,就会触发报错,也会打破你“只执行一次”的预期。
修复步骤
1. 先修正Hooks调用位置
所有Hooks必须直接写在组件函数的最外层,不能被任何逻辑块包裹。
错误写法示例:
// 子组件 function DataPanel(props) { // 错误:useEffect写在if条件内部 if (props.list) { useEffect(() => { console.log(props.list) }, []) } return <div>{/* 渲染逻辑 */}</div> }
正确写法是把条件判断移到useEffect回调内部:
function DataPanel(props) { useEffect(() => { // 条件判断放在effect内部 if (props.list) { console.log(props.list) } }, [props.list]) // 用到的props必须加入依赖数组 return <div>{/* 渲染逻辑 */}</div> }
2. 实现“仅打印一次数据”的逻辑
不要用空依赖数组强行卡执行时机,配合useRef做标记位,既能保证拿到数据后打印,也不会重复执行:
import { useEffect, useRef, useState } from 'react' import axios from 'axios' function DataPanel() { // 用ref存执行标记,修改不会触发重渲染 const hasPrint = useRef(false) const [list, setList] = useState([]) useEffect(() => { // 已经打印过、或者数据还没返回就直接终止 if (hasPrint.current || list.length === 0) return console.log('接口返回数据:', list) hasPrint.current = true }, [list]) // 接口请求逻辑 useEffect(() => { const controller = new AbortController() axios.get('/api/list', { signal: controller.signal }) .then(res => setList(res.data)) // 写清理逻辑,严格模式下卸载时取消重复请求,避免报错 return () => controller.abort() }, []) return <div>{/* 渲染逻辑 */}</div> }
补充:严格模式下的双执行只在开发环境出现,生产环境不会重复挂载组件,只要副作用写了对应的清理逻辑,不会影响线上功能。
内容的提问来源于stack exchange,提问作者Tamar Laufer
相关产品推荐
相关产品推荐

