React Native中BottomSheet Modal点击触发内部错误求助
错误信息
Warning: Internal React error: Attempted to capture a commit phase error inside a detached tree. This indicates a bug in React. Likely causes include deleting the same fiber more than once, committing an already-finished tree, or an inconsistent return pointer.
TypeError: undefined is not a function
in BottomSheet (created by BottomSheet)
错误分析
该错误是由多方面问题共同引发的:闭包导致的过时引用、组件属性名不匹配、空值调用函数/属性、条件逻辑漏洞,最终导致BottomSheet组件状态异常,触发React内部的detached tree错误。
具体修复步骤
1. 修复renderBackdrop的闭包依赖问题
useCallback的依赖数组未包含fRef和props.animatedPosition,导致闭包捕获的是过时值,调用dismiss时可能找不到正确组件实例:
const renderBackdrop = useCallback((props) => { const opacityStyle = useAnimatedStyle( () => ({ opacity: (800 - props.animatedPosition.value) / 896, }), [props.animatedPosition] // 加入动画位置依赖 ); return ( <Animated.View style={[ StyleSheet.absoluteFillObject, { backgroundColor: "#000000", borderWidth: 1 }, opacityStyle, ]} > <TouchableOpacity onPress={() => fRef.current?.dismiss()} style={StyleSheet.absoluteFillObject} /> </Animated.View> ); }, [fRef]); // 加入ref到依赖数组
2. 统一选项的图标字段名
resend选项使用了小写icon字段,其他选项都是大写Icon,如果ActionListContainer期望大写属性,会导致渲染错误:
// 原resend选项 options.push({ id: "resend", title: "Resend message", icon: Resend, // 改为大写Icon handler: callHandlerAndDismissSheet.bind(null, actionHandlers?.resendMessage), }); // 修改后 options.push({ id: "resend", title: "Resend message", Icon: Resend, handler: callHandlerAndDismissSheet.bind(null, actionHandlers?.resendMessage), });
3. 处理chatClient空值情况
调用pinMessage/unpinMessage前先判断chatClient是否存在,避免调用undefined的方法:
const pin = async () => { if (!chatClient) return; await chatClient.pinMessage(message, null); }; const unpin = async () => { if (!chatClient) return; await chatClient.unpinMessage(message); };
4. 安全调用propMessageReactionPicker
添加可选链调用,避免未传递该函数时触发错误:
const openReactionPicker = () => { propMessageReactionPicker?.(message); };
5. 优化pin/unpin的条件判断逻辑
简化判断并确保message.pinned_by存在再访问属性:
else { const isPinned = message.pinned ?? false; if (!isPinned) { options.push({ Icon: Pin, handler: callHandlerAndDismissSheet.bind(null, pin), id: "pin/unpin", title: Title, }); } else { if (message.pinned_by && chatClient.user.id === message.pinned_by.id) { options.push({ Icon: Unpin, handler: callHandlerAndDismissSheet.bind(null, unpin), id: "pin/unpin", title: Title, }); } } }
6. 全局空值防护
在组件开头添加空值判断,避免后续访问属性时出错:
export const MessageActionSheetSlack = React.forwardRef((props, fRef) => { const { activeMessage: message, activeChannel: channel } = useContext(AuthContext); const { actionHandlers, openReactionPicker: propMessageReactionPicker } = props; const chatClient = ChatClientService.getClient(); const navigation = useNavigation(); const actionHandlersExist = !!actionHandlers; // 空值直接返回null,避免后续报错 if (!actionHandlersExist || !message || !channel || !chatClient) { return null; } // ... 后续代码 });
总结
以上修复解决了闭包引用、属性不匹配、空值调用、逻辑漏洞等问题,组合起来就能消除点击操作时的报错。
内容的提问来源于stack exchange,提问作者Nishada Dilshan Weerathunga

