如何重构代码实现React Hooks条件调用并修复Lint报错
解决React Hooks条件调用导致的Lint报错问题
你遇到的Lint报错源于违反了React Hooks的核心规则:Hooks必须在组件顶层作用域调用,且每次渲染的调用顺序必须完全一致。当前代码中两个useCallback被放在条件return语句之后,当组件触发提前return时,这些Hooks不会被执行,导致不同渲染周期中Hooks的调用顺序/数量不一致,触发了规则校验。
重构方案
- 将所有Hooks调用移到组件最顶部,放在任何条件判断和return语句之前,确保每次渲染都按相同顺序执行Hooks。
- 修正
useCallback的依赖数组,确保所有用到的外部变量都被包含(避免闭包导致的 stale 值问题)。 - 保留
useCallback内部的条件逻辑,确保组件功能和原本一致。
重构后的完整代码
const BreakdownProductsWidget = ({ b2b2c }) => { // 1. 将Hooks移到组件最顶层,保证每次渲染都按顺序调用 const getCakeTypeOptions = useCallback(() => { if (IsPackage && CoverType && Products) { const items = filterCakeItemsToDisplay(Products); return ( <> {renderCakeHeader(items)} {renderCakeOptions(items)} </> ); } return null; }, [Products, IsPackage, CoverType, filterCakeItemsToDisplay, renderCakeHeader, renderCakeOptions]); // 修正依赖数组:添加b2b2c(函数内部用到了该props) const getB2B2COptions = useCallback(() => { if (b2b2c) { return Products?.map((product, index) => ( <CheckListWidget ... /> )); } return null; }, [Products, b2b2c]); // 2. 保留原本的条件return逻辑不变 if (!coverProduct && !IsPayg && !b2b2c) { return null; } if (!coverProduct && IsPayg) { return ( <RestrictedCustomersWidget ... /> ); } const filterCakeItemsToDisplay = (arrayOfItems) => arrayOfItems.filter( (item) => item.isActive && (item?.productType === BreakdownProductEnum.COVER_OPTIONS || item?.productType === BreakdownProductEnum.COVER_PACKAGE), ); const renderCakeHeader = (items) => items?.map((product, index) => ( <CheckListWidget ... /> )); const renderCakeOptions = (items) => items?.map((product) => product?.Benefits?.map((benefitName, index) => ( <CheckListWidget ... /> )), ); const Wrapper = b2b2c ? View : TouchableOpacity; const b2b2cHasOnlyLegalProducts = b2b2c && Products?.every((x) => x.isLegalInsuranceProduct); return ( <View style={panel.container} {...AccessibilityHelper('Breakdown widget container')}> <Wrapper onPress={() => navigate(AppRoutes.Offers.Name)}> <View style={panel.titleWrapper}> <Text style={panel.title} {...AccessibilityHelper('Breakdown widget cover type')}> {b2b2cHasOnlyLegalProducts ? 'Your cover' : coverProduct?.productName} </Text> </View> {b2b2cHasOnlyLegalProducts ? null : ( <> <Text ...> {coverProduct?.text?.covered?.short} </Text> </> )} {getCakeTypeOptions()} {getB2B2COptions()} {coverOptions && coverOptions .filter((p) => p.isActive) .map((product, index) => ( <CheckListWidget ... /> ))} {b2b2c ? null : ( <> <Text ...> {breakdownProducts.breakdownExtras} </Text> {activeBreakdown && Array.isArray(activeBreakdown) && activeBreakdown.length > 0 ? ( activeBreakdown.map((product, index) => ( <CheckListWidget ... /> )) ) : ( <Text style={boxWidget.subTitle}> {breakdownProducts.noBreakdownExtras} </Text> )} <CallToActionWidget linkTitle={membershipOverview.breakdownCoverCallToAction} /> </> )} </Wrapper> {state.showModal && ( <MessageModals ... /> )} </View> ); }; export default BreakdownProductsWidget; BreakdownProductsWidget.propTypes = { b2b2c: PropTypes.bool, }; BreakdownProductsWidget.defaultProps = { b2b2c: false, };
关键修改说明
- Hooks位置调整:将
useCallback移到组件最开头,确保无论组件是否提前return,Hooks都会被调用,满足调用顺序一致的规则。 - 依赖数组修正:为
getB2B2COptions添加b2b2c依赖,因为函数内部直接使用了该props,避免闭包导致的状态不一致问题。 - 功能一致性:所有原本的条件判断逻辑都保留在
useCallback内部,组件的渲染行为和原有逻辑完全一致。
内容的提问来源于stack exchange,提问作者Bomber
相关产品推荐
相关产品推荐

