React useEffect无法仅执行一次,监听generalInfo未变更仍触发重渲染
无限重渲染问题根因
你最初的useEffect写法存在循环触发逻辑,导致组件不断重渲染:
- 你的useEffect依赖项为
generalInfo,且回调内先调用onGetGeneralInfo发起接口请求 - 接口请求成功后,saga会分发
GET_GENERAL_INFO_SUCCESSaction,reducer会更新redux中的generalInfo值 generalInfo更新后会触发依赖它的useEffect再次执行,再次发起请求、更新generalInfo,形成无限循环,最终表现为组件不停重渲染
修复方案逻辑说明
你给出的拆分两个useEffect的写法是正确的,逻辑如下:
// 仅组件挂载时执行一次,发起初始数据请求,不会重复触发 useEffect(() => { onGetGeneralInfo(); }, []); // 仅当generalInfo变更时同步更新本地data状态 useEffect(() => { setData(generalInfo); }, [generalInfo, setData]);
- 第一个useEffect依赖为空数组,只会在组件首次挂载后执行一次,避免重复发起接口请求
- 第二个useEffect的依赖项中,
setData是useState返回的稳定函数引用,不会主动触发effect执行;只有当redux中的generalInfo真实发生变更时,才会执行setData更新本地状态,完全符合你期望的「只有generalInfo变更时data才同步更新」的需求
你提供的其他关联代码逻辑均无问题,不需要额外调整:
- mapStateToProps正确关联了全局state中的generalInfo到组件props
const mapStateToProps = state => { const {general} = state; return { generalInfo: general.generalInfo, }; };
- mapDispatchToProps正确绑定了action创建函数
const mapDispatchToProps = dispatch => { return { onGetGeneralInfo: bindActionCreators(getGeneralInfo, dispatch), }; };
- reducer的
GET_GENERAL_INFO_SUCCESS处理逻辑正确,更新后会触发关联组件的props更新
case GET_GENERAL_INFO_SUCCESS: { const {payload} = action; return { ...state, generalInfo: payload, }; }
- saga的请求逻辑正常,请求成功后会正确分发action更新全局状态
export function* getGeneralInfoSaga() { try { const tokenKey = yield AsyncStorage.getItem('tokenKey'); const userId = yield AsyncStorage.getItem('userId'); const params = { method: 'GET', headers: { Authorization: `Bearer ${tokenKey}`, }, }; const response = yield call( fetch, `${API_GET_GENERAL_INFO}?id=${userId}`, params, ); const body = yield call([response, response.json]); if (response.status === 200) { yield put(getGeneralInfoSuccess(body)); } else { yield put(getGeneralInfoFail()); throw new Error(response); } } catch (error) { yield put(getGeneralInfoFail()); console.log(error); } }
可选优化建议
如果你本地的data状态不需要做独立于全局redux的修改,其实可以直接使用props传入的generalInfo,不需要额外维护本地state,减少冗余代码。
内容的提问来源于stack exchange,提问作者Zuet
相关产品推荐
相关产品推荐

