You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构代码实现React Hooks条件调用并修复Lint报错

解决React Hooks条件调用导致的Lint报错问题

你遇到的Lint报错源于违反了React Hooks的核心规则:Hooks必须在组件顶层作用域调用,且每次渲染的调用顺序必须完全一致。当前代码中两个useCallback被放在条件return语句之后,当组件触发提前return时,这些Hooks不会被执行,导致不同渲染周期中Hooks的调用顺序/数量不一致,触发了规则校验。

重构方案

  1. 将所有Hooks调用移到组件最顶部,放在任何条件判断和return语句之前,确保每次渲染都按相同顺序执行Hooks。
  2. 修正useCallback的依赖数组,确保所有用到的外部变量都被包含(避免闭包导致的 stale 值问题)。
  3. 保留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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 10:19:16