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> ); };
解决方案
错误原因分析
- Cartmodal组件内部状态冲突:组件同时接收父组件的
cartmodalVisibleprops,又自己维护visible状态,导致状态不同步;且TouchableOpacity的onPress直接执行setVisible(!visible),相当于渲染时立即触发状态更新,引发重渲染循环。 - 重复渲染组件: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
相关产品推荐
相关产品推荐

