通过cloneElement传递react-native-modal的mounted状态失效问题排查
你的代码核心问题是组件层级搞反了:
你封装的自定义Modal组件,是给它自己的直接子元素(也就是你在<Modal>标签里写的内容)传递isMounted属性,但你的MyModal是一个包裹了自定义Modal的外层组件,并不是自定义Modal的子组件,自然接收不到这个属性。
看你的代码结构:
// MyModal的结构 const MyModal = forwardRef(({isMounted, ...props}, ref) => { return ( <Modal ref={ref}> <View>...</View> {/* 这才是自定义Modal的直接子元素 */} </Modal> ); });
自定义Modal里的React.Children.map遍历的是<Modal>内部的<View>,所以isMounted会传给这个<View>,而不是外层的MyModal。
另外,你代码里还有个小拼写错误:iseNativeDriver应该改成useNativeDriver,否则动画可能出问题。
解决方案1:让MyModal拿到Modal的可见状态
如果你需要让MyModal感知到自定义Modal的显示/隐藏状态,可以通过以下方式修改:
方式A:给自定义Modal添加状态回调
在自定义Modal里添加一个onVisibilityChange props,当isVisible变化时触发:
export default Modal = forwardRef(({children, onVisibilityChange, ...props}, ref) => { const [isVisible, setIsVisible] = useState(false); useEffect(() => { onVisibilityChange?.(isVisible); }, [isVisible, onVisibilityChange]); useImperativeHandle( ref, () => ({ show: () => setIsVisible(true), hide: () => setIsVisible(false), }), [], ); const childrenWithProps = React.Children.map(children, child => { return React.cloneElement(child, {isMounted: isVisible}); }); return ( <RNModal isVisible={isVisible} animationInTiming={200} animationOutTiming={200} useNativeDriver={true} // 修正拼写错误 hideModalContentWhileAnimating={true} onBackdropPress={() => ref.current?.hide()} onBackButtonPress={() => ref.current?.hide()} {...props} > {childrenWithProps} </RNModal> ); });
然后在MyModal里使用这个回调:
const MyModal = forwardRef((props, ref) => { const [isMounted, setIsMounted] = useState(false); useEffect(() => { console.log(isMounted); }, [isMounted]); return ( <Modal ref={ref} onVisibilityChange={setIsMounted} > <View>...</View> </Modal> ); });
方式B:通过ref直接获取状态(不推荐)
可以在useImperativeHandle里暴露isVisible状态,但这种方式违反了React的单向数据流原则,尽量少用:
useImperativeHandle( ref, () => ({ show: () => setIsVisible(true), hide: () => setIsVisible(false), get isVisible() { return isVisible; } }), [isVisible], // 依赖添加isVisible );
然后在MyModal里通过ref.current?.isVisible获取,但需要注意状态同步问题。
解决方案2:让Modal内部的子组件拿到isMounted
如果你的需求是让<Modal>内部的子组件(比如那个<View>所在的组件)接收isMounted,那只需要把需要这个属性的组件放在自定义Modal的children里:
// 定义一个需要isMounted的子组件 const ModalContent = ({isMounted}) => { useEffect(() => { console.log(isMounted); }, [isMounted]); return <View>...</View>; }; // 使用时 const MyModal = forwardRef((props, ref) => { return ( <Modal ref={ref}> <ModalContent /> {/* 这里会自动接收到isMounted属性 */} </Modal> ); });
这样ModalContent作为自定义Modal的直接子元素,就能通过React.cloneElement拿到isMounted属性了。
内容的提问来源于stack exchange,提问作者Sam Leurs

