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

React项目计算保单最大折扣百分比报错#301求修复方案

React组件获取最大折扣百分比报错的修复思路

错误信息

react-dom.production.min.js:5309 Error: Minified React error #301; visit https://reactjs.org/docs/error-decoder.html?invariant=301 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at ta (react-dom.production.min.js:3855:1)
    at zu (react-dom.production.min.js:6955:1)
    at Oc (react-dom.production.min.js:6411:1)
    at bc (react-dom.production.min.js:6400:1)
    at yc (react-dom.production.min.js:6382:1)
    at lc (react-dom.production.min.js:6103:4)
    at react-dom.production.min.js:2898:1
    at t.unstable_runWithPriority (scheduler.production.min.js:289:1)
    at Uo (react-dom.production.min.js:2868:1)
    at Wo (react-dom.production.min.js:2893:1)
overrideMethod @ console.js:213
ou @ react-dom.production.min.js:5309
n.callback @ react-dom.production.min.js:5329
sit @ react-dom.production.min.js:3182
fu @ react-dom.production.min.js:5463
(anonymous) @ react-dom.production.min.js:6687
t.unstable_runWithPriority @ scheduler.production.min.js:289
Uo @ react-dom.production.min.js:2868
Sc @ react-dom.production.min.js:6470
lc @ react-dom.production.min.js:6109
(anonymous) @ react-dom.production.min.js:2898
t.unstable_runWithPriority @ scheduler.production.min.js:289
Uo @ react-dom.production.min.js:2868
Wo @ react-dom.production.min.js:2893
Vo @ react-dom.production.min.js:2883
fc @ react-dom.production.min.js:6134
notify @ Subscription.js:23
t.notifyNestedSubs @ Subscription.js:63
t.handleChangeWrapper @ Subscription.js:68
g @ redux.js:220
(anonymous) @ index.js:11
dispatch @ redux.js:638
(anonymous) @ insuranceReducer.ts:146
l @ runtime.js:64
(anonymous) @ runtime.js:281
(anonymous) @ runtime.js:117
r @ asyncToGenerator.js:3
u @ asyncToGenerator.js:25
Show 1 more frame
Show less
react-dom.production.min.js:3855 Uncaught (in promise) Error: Minified React error #301; visit https://reactjs.org/docs/error-decoder.html?invariant=301 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at ta (react-dom.production.min.js:3855:1)
    at zu (react-dom.production.min.js:6955:1)
    at Oc (react-dom.production.min.js:6411:1)
    at bc (react-dom.production.min.js:6400:1)
    at yc (react-dom.production.min.js:6382:1)
    at lc (react-dom.production.min.js:6103:4)
    at react-dom.production.min.js:2898:1
    at t.unstable_runWithPriority (scheduler.production.min.js:289:1)
    at Uo (react-dom.production.min.js:2868:1)
    at Wo (react-dom.production.min.js:2893:1)

问题代码片段

const [maxPercentage, setMaxPercentage] = useState([] as any);
data.map((d) => setMaxPercentage(getDiscountPercentage(d.amount,d.discountedAmount).toFixed(0)))

<div>Знижка {Math.max(...maxPercentage)}%</div>

完整Policy组件代码

import React, { useState } from "react";
import { ICompanyInsurance } from "../../typings/IDataOffers";
import { useDispatch, useSelector } from "react-redux";
import { getSelectedAmountItem, getDiscountPercentage, getCashBack } from "../../redux-data/insurance/insuranceSelector";
import { setIdPolicy } from "../../redux-data/insurance/insuranceReducer";
import { companyImage, companyImages } from "../../constants/companyImages";

//import im from '../../assets/img/providna.webp';

interface IPolicy extends ICompanyInsurance {
  formId?: string;
  index?: number;
  onBuy?: Function;
  onPay?: Function;
}

const Policy = ({ name, data, formId, id, onBuy, onPay, contractorId, companyId }: IPolicy) => {
  const getDiscountPercentage = (payment, discountedPayment) => {
    return ((payment - discountedPayment) / payment) * 100;
  };

  const dispatch = useDispatch()
  const min = Math.min(...data.map((d) => d.amount));
  const max = Math.max(...data.map((d) => d.amount));

  const minDiscount = Math.min(...data.map((d) => d.discountedAmount));
  const maxDiscount = Math.max(...data.map((d) => d.discountedAmount));
  
  const policy = useSelector(getSelectedAmountItem);
  const price = policy ? policy.amount : min !== max ? `${min.toFixed(2)} - ${max.toFixed(2)}` : min.toFixed(2);
  const discountedPrice = policy ? policy.discountedAmount : minDiscount !== maxDiscount ? `${minDiscount.toFixed(2)} - ${maxDiscount.toFixed(2)}` : minDiscount.toFixed(2);
  const [maxPercentage, setMaxPercentage] = useState([] as any);
  data.map((d) => setMaxPercentage(getDiscountPercentage(d.amount, d.discountedAmount).toFixed(0)));
  const cashBackPercentage = 10;
  const greenCardCashBack = getCashBack(price, cashBackPercentage);

  const handleClick = () => {
    if (onPay) {
      onPay();
    } else if (onBuy) {
      dispatch(setIdPolicy(contractorId))
      onBuy(id);
    }
  };
   
  const img = companyImages.find((company: companyImage) => company.id === companyId)
 
  return (
    <div className="search-result_one">
      <figure className="search-result_pic">
        <img src={img ? require(`../../assets/img/logos/${img.key}`) : "\img\base.webp"} alt="" />
      </figure>
      <article className="search-result_info">
        <h4>{name}</h4>
        {min > minDiscount || max > maxDiscount ? 
          <div className="search-result_price">
            <div className="old-price">{price} ₴</div>
            <div>Знижка {Math.max(...maxPercentage)}%</div>
            <div>До сплати {discountedPrice} ₴</div>
          </div> 
          : 
          <div className="search-result_price">{price} ₴ 
            {data.map((item, index) => item.isCashBack && index < 1 ? <div className="cash-back-item">Кешбек на Вашу карту {`${greenCardCashBack.toFixed(2)} ₴ (${cashBackPercentage}%)`}</div> : "")}
          </div>
        }
      </article>
      <div className="search-result_right">
        <button
          form={formId}
          type="submit"
          className="btn btn-link btn-primary btn-buy"
          onClick={handleClick}
        >
          {!formId ? "Вибрати" : "Оплатити"}{" "}
          <i className="fa fa-long-arrow-right" aria-hidden="true"></i>
        </button>
      </div>
    </div>
  );
};

export default Policy;

修复思路与代码修改

1. 解决无限重渲染循环

直接在组件渲染阶段调用setMaxPercentage会触发组件重新渲染,每次渲染又会重复调用setState,形成无限循环,这是触发错误的核心原因。不需要用useState存储折扣数组,直接计算最大折扣值即可。

2. 修正数值类型错误

toFixed(0)会将数字转为字符串,而Math.max无法对字符串数组取最大值,导致逻辑错误。应该先计算数值类型的折扣百分比,取最大值后再转为字符串展示。

3. 优化计算逻辑(可选)

使用useMemo缓存计算结果,只有当data变化时才重新计算,提升组件性能。

修改后的代码示例

替换原来的useState和data.map部分:

import React, { useState, useMemo } from "react";

// ... 其他代码保持不变

const Policy = ({ name, data, formId, id, onBuy, onPay, contractorId, companyId }: IPolicy) => {
  // ... 其他代码保持不变

  // 计算最大折扣百分比,处理空数据边界情况
  const maxPercentage = useMemo(() => {
    if (data.length === 0) return 0;
    const percentages = data.map(d => getDiscountPercentage(d.amount, d.discountedAmount));
    return Math.max(...percentages);
  }, [data]);

  // ... 其他代码保持不变

  return (
    // ... 其他代码保持不变
    <div>Знижка {Math.floor(maxPercentage)}%</div>
    // ... 其他代码保持不变
  );
};

额外边界处理

如果payment可能为0,需要在getDiscountPercentage里处理除以0的情况:

const getDiscountPercentage = (payment, discountedPayment) => {
  if (payment === 0) return 0;
  return ((payment - discountedPayment) / payment) * 100;
};

内容的提问来源于stack exchange,提问作者Hunter91151

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 17:29:53