如何在useEffect中区分触发源是props变更还是组件内部状态变更
问题根源
你当前的useEffect依赖数组同时包含了props传入的propState和组件内部状态innerState,只要两者任意一个发生变更都会触发effect执行,自然会导致内部状态更新和props变更的处理逻辑耦合。同时effect内部还会修改innerState,极端场景下甚至会出现反复触发effect的死循环问题。
解决方案
根据你的实际需求选择对应方案即可:
方案1:每次propState变更时同步内部状态(最常用,适配Modal每次打开同步最新props的场景)
直接从依赖数组中移除innerState即可,你使用了函数式更新setState,本身就能拿到最新的内部状态,不需要将innerState加入依赖:
useEffect(() => { if (Object.getOwnPropertyNames(propState).length !== 0) { console.log(propState); setState(prev => { // 函数式更新直接返回新状态即可,不需要直接修改innerState变量 return { ...prev, innerState: propState.user_name } }); } // 仅保留propState作为依赖,只有props变更时才会触发effect }, [propState]);
方案2:仅在propState第一次非空时执行一次(后续propState变更不再处理)
如果只需要初始化一次内部状态,之后完全由组件内部控制状态,可以加ref标记执行状态:
// 标记逻辑是否已经执行过 const hasInitedRef = useRef(false); useEffect(() => { if (!hasInitedRef.current && Object.getOwnPropertyNames(propState).length !== 0) { console.log(propState); setState(prev => ({ ...prev, innerState: propState.user_name })); hasInitedRef.current = true; } }, [propState]);
方案3:propState内容真正变化时才执行(避免引用变化但内容不变的多余触发)
如果父组件可能传入引用不同但内容完全一致的propState,可以加ref存储上一次的propState做内容比较:
const prevPropStateRef = useRef(propState); useEffect(() => { // 此处可根据需求替换为深比较逻辑 const isContentChanged = JSON.stringify(prevPropStateRef.current) !== JSON.stringify(propState); if (isContentChanged && Object.getOwnPropertyNames(propState).length !== 0) { console.log(propState); setState(prev => ({ ...prev, innerState: propState.user_name })); prevPropStateRef.current = propState; } }, [propState]);
内容的提问来源于stack exchange,提问作者bihire boris
相关产品推荐
相关产品推荐

