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

React Native点击添加购物车按钮打开弹窗时出现重复渲染报错求助

问题:React Native弹窗触发无限重渲染报错

开发服务详情页面时,点击「添加到购物车」按钮期望打开Cartmodal弹窗,但触发了报错:Too many re-renders. React limits the number of renders to prevent an infinite loop,尝试多种方法无法修复,求解决。

ServiceDetailsScreen组件代码

const ServiceDetailsScreen = ({navigation, route}) => {
  const [data, setData] = useState({});
  const [LabData, setLabData] = useState({});
  const [TestData, setTestData] = useState({});
  const [Service, setService] = useState({});
  const [cartmodalVisible, setcartmodalVisible] = useState(false);
  const [bookNowModal, setbookNowModal] = useState(false);
  const id = route.params.id;

  const getServiceDetails = async () => {
    try {
      console.log('id of service', route.params.id);
      const {data} = await axios.get(
        `${url}/Inventory/getServiceDetail/${id}`,
      );
      setData(data);
      setLabData(data.LabData);
      setTestData(data.TestData);
    } catch (error) {
      console.log(error);
      ToastAndroid.show('Something went wrong', ToastAndroid.SHORT);
    }
  };

  useEffect(() => {
    getServiceDetails();
  }, [id]);

  return (
    <View style={Styles.ServiceContainer}>
      <Cartmodal cartmodalVisible={cartmodalVisible} />
    
      <View style={Styles.Service}>
        <View style={Styles.Picture}>
          <View>
            <Image source={logo} style={Styles.Logo} />
          </View>
          <View style={{flexShrink: 1}}>
            <Text style={{fontSize: 18, flexShrink: 1}}>{LabData.name}</Text>
          </View>
        </View>
      </View>
      <View style={Styles.Service}>
        <View style={Styles.DetailsHeader}>
          <Text style={{fontWeight: 'bold', fontSize: 25}}>
            {data.ServiceTitle}
          </Text>
          <Text style={{fontWeight: '500', fontSize: 20}}>
            PKR: {data.ServicePrice}
          </Text>
        </View>
        <View style={Styles.RatingContainer}>
          <Rating imageSize={14} readonly startingValue={data.rating} />
        </View>
        <View style={Styles.DetailDescription}>
          <Text style={Styles.headings}>Description:</Text>
          <Text style={Styles.DetailDescriptionText}>
            {data.ServiceDescription}
          </Text>
        </View>
        <View style={Styles.DetailDescription}>
          <Text style={Styles.headings}>Pre Conditions:</Text>
          <Text style={Styles.DetailDescriptionText}>
            {TestData.PreConditions}
          </Text>
        </View>
        <View style={Styles.DetailDescription}>
          <Text style={Styles.headings}>Average Duration:</Text>
          <Text style={Styles.DetailDescriptionText}> {data.Duration}</Text>
        </View>
        <View style={Styles.DetailDescription}>
          <Text style={Styles.headings}>Additional Services:</Text>

          <View style={Styles.AdditionalServices}>
            <Text style={Styles.DetailDescriptionText}>
              Home Sampling:{' '}
              {data.HomeSampling == true ? (
                <Text Styles={{fontWeight: 'bold'}}>✓ </Text>
              ) : (
                <Text Styles={{fontWeight: 'bold'}}>✗ </Text>
              )}
            </Text>
            {data.HomeSampling == true ? (
              <Text style={Styles.PriceText}>
                PKR: {data.HomeSamplingPrice}
              </Text>
            ) : null}
          </View>
          <View style={Styles.AdditionalServices}>
            <Text style={Styles.DetailDescriptionText}>
              Communication:
              {data.Communication == true ? (
                <Text Styles={{fontWeight: 'bold'}}>✓ </Text>
              ) : (
                <Text Styles={{fontWeight: 'bold'}}>✗ </Text>
              )}
            </Text>
            {data.Communication == true ? (
              <Text style={Styles.PriceText}>
                PKR: {data.CommunicationPrice}
              </Text>
            ) : null}
          </View>
        </View>
        <Cartmodal cartmodalVisible={cartmodalVisible} />
        <View style={Styles.Buttons}>
          <Button
            type="solid"
            title="Book Now"
            buttonStyle={{
              backgroundColor: '#34C38F',
              borderColor: 'transparent',
              borderWidth: 0,
              paddingVertical: 7,
              borderRadius: 10,
            }}
            titleStyle={{fontWeight: '100'}}
            icon={{
              name: 'timer',
              type: 'FontAwesome',
              size: 20,
              color: 'white',
            }}></Button>

          <Button
            type="solid"
            title="Add To Cart"
            onPress={() => setcartmodalVisible(true)}
            buttonStyle={{
              backgroundColor: '#0154E0',
              borderColor: 'transparent',
              borderWidth: 0,
              paddingVertical: 7,
              borderRadius: 10,
            }}
            titleStyle={{fontWeight: '100'}}
            icon={{
              name: 'shopping-cart',
              type: 'MaterialIcons',
              size: 20,
              color: 'white',
            }}></Button>
        </View>
      </View>
    </View>
  );
};

export default ServiceDetailsScreen;

Cartmodal组件代码

const Cartmodal = props => {
  const {cartmodalVisible} = props;
  const [visible, setVisible] = useState(cartmodalVisible);
  console.log('cartmodalVisible', cartmodalVisible);

    return (
      <Modal
        animationType="slide"
        transparent={true}
        visible={visible}
        onRequestClose={() => {
          setVisible(!visible);
        }}
        onDismiss={() => {
          setVisible(!visible);
        }}
      >
        <View style={modalstyles.centeredView}>
          <View style={modalstyles.modalView}>
            <View style={modalstyles.modalHeader}>
              <View style={modalstyles.modalHeaderContent}>
                <Text>Add to Cart</Text>
              </View>
              <TouchableOpacity onPress={setVisible(!visible)}>
                <Text style={modalstyles.modalHeaderCloseText}>X</Text>
              </TouchableOpacity>
            </View>
            <Text style={modalstyles.modalText}>Hello World!</Text>
            <Pressable style={[modalstyles.button, modalstyles.buttonClose]}>
              <Text style={modalstyles.textStyle}>Cancel</Text>
            </Pressable>
          </View>
        </View>
      </Modal>
    );
 
};

解决方案

错误原因分析

  1. Cartmodal组件内部状态冲突:组件同时接收父组件的cartmodalVisible props,又自己维护visible状态,导致状态不同步;且TouchableOpacity的onPress直接执行setVisible(!visible),相当于渲染时立即触发状态更新,引发重渲染循环。
  2. 重复渲染组件:ServiceDetailsScreen中重复渲染了两次<Cartmodal />,增加不必要的渲染开销,也可能加剧状态冲突。

修复步骤

1. 重构Cartmodal组件,由父组件统一控制状态

移除组件内部的visible状态,直接使用父组件传入的cartmodalVisible,并通过props接收关闭弹窗的方法:

const Cartmodal = props => {
  const {cartmodalVisible, onClose} = props;

  return (
    <Modal
      animationType="slide"
      transparent={true}
      visible={cartmodalVisible}
      onRequestClose={onClose}
      onDismiss={onClose}
    >
      <View style={modalstyles.centeredView}>
        <View style={modalstyles.modalView}>
          <View style={modalstyles.modalHeader}>
            <View style={modalstyles.modalHeaderContent}>
              <Text>Add to Cart</Text>
            </View>
            <TouchableOpacity onPress={onClose}>
              <Text style={modalstyles.modalHeaderCloseText}>X</Text>
            </TouchableOpacity>
          </View>
          <Text style={modalstyles.modalText}>Hello World!</Text>
          <Pressable style={[modalstyles.button, modalstyles.buttonClose]} onPress={onClose}>
            <Text style={modalstyles.textStyle}>Cancel</Text>
          </Pressable>
        </View>
      </View>
    </Modal>
  );
};

2. 修改ServiceDetailsScreen组件,移除重复渲染并传递关闭方法

只保留一个<Cartmodal />实例,同时传递onClose方法让父组件控制弹窗状态:

return (
  <View style={Styles.ServiceContainer}>
    {/* 只保留这一个Cartmodal实例 */}
    <Cartmodal 
      cartmodalVisible={cartmodalVisible} 
      onClose={() => setcartmodalVisible(false)} 
    />
    
    {/* 原有其他代码保持不变 */}
    
    {/* 移除此处重复的<Cartmodal cartmodalVisible={cartmodalVisible} /> */}
    <View style={Styles.Buttons}>
      {/* 原有按钮代码保持不变 */}
    </View>
  </View>
);

修复原理

  • 让父组件统一管理弹窗的显示状态,保持单一数据源,避免子组件内部状态与props冲突。
  • 将弹窗关闭逻辑委托给父组件,避免子组件渲染时直接触发状态更新,从而解决无限重渲染问题。
  • 移除重复组件渲染,优化性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:25:19