React Native modal is opening child WebBrowser and Share native views only after closing modal - react-native

I use React-native and Expo for my mobile client.
Flow:
User taps to open up a modal from root view
Modal has Expo's Share and WebView invoking buttons.
When user taps to open either of them, nothing happens.
When the modal is closed manually by the user, then the WebBrowser/Share are suddenly invoked, and opened on the root view that the modal is a child of.
Both functions to call the Share and WebBrowser invokers are in the modal's controller.
Partial Content of the modal controller:
openBrowser = (url) => {
WebBrowser.openBrowserAsync(url);
}
shareModel = (url) => {
Share.share({
url: url
}) .then(console.log(`Shared ${url}`))
.catch(err => console.log(err))
}
render() {
return (
<Modal
visible={this.props.visible}
animationType={'slide'}
onRequestClose={this.props.onRequestClose}
>
<View style={styles.modalContainer}>
<View style={styles.oneButton}>
<TouchableOpacity style={styles.toolbarButton} onPress={() => this.openBrowser(this.props.model.public_url)}>
<SimpleLineIcons
name='globe'
size={29}
style={{ marginTop: 1 }}
color='#7366E3'
/>
<Text style={{ color: '#7366E3', fontSize: 13, marginTop: 1 }}>View</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)
}
Any ideas? This exact code worked well in the past and something changed.

It could be due to the new Expo client's SDK26. One of their breaking changes include methods in the WebBrowser, see https://blog.expo.io/expo-sdk-v26-0-0-is-now-available-2be6d9805b31

Related

Present modal inside a screen

I'm creating an application where I'm using a MaterialBottomTabBar and in the Home Screen I've a button which are supposed to open up a <Modal>.
After investigating this for quite some time I found out that I cannot present the modal when using the tabbar. I've tried to put the modal in all my tabbar-screens (without any success). And then moved the modal over to another screen which are not included in the tab-bar and the modal worked perfectly fine.
export default function Home({ route, navigation }){
const [modal, setModal] = useState(false)
return(
<View style={styles.container}>
<Modal visible={modal} transparent={true}>
<View style={{height: "100%", width: "80%", backgroundColor: "blue"}}>
<Text>TIFMNASIFAMNFISAMFIAOFMSAOFMAFOPSAMF</Text>
</View>
</Modal>
<TouchableOpacity style={styles.addNewButton} onPress={() => setModal(true)}>
<Text style={styles.addNewButtonText}>+</Text>
</TouchableOpacity>
</View>
)
}
As you can see i'm using the React Native Hooks useState(false) and refers to it inside the Modal. But whenever I tap the <TouchableOpacity> the modal is not being presented.
NOTE!
The modal is being presented if I replace the
<Modal visible={modal} transparent={true}>
to
<Modal visible={true} transparent={true}>
But would be runned instantly, which is not an option in my case.
Do you have any idea how I can run this modal inside the screen?

How to identify which view is clicked programmatically

I am developing an android application on react native. Which has several buttons and I want to perform different actions on click of each button.
I am quite familiar with android sdk, in android sdk android:id attribute is present to identify components. But the problem in react native is how to identify which button is clicked by user.
<View style = { styles.button_container } >
<Text
onPress={() => } >Component
</Text>
<Text
onPress={() => } >About
</Text>
</View>
you can send a parameter with the button clicked, something like this:
function MenuComponent(props) {
return (
<View style = { styles.button_container } >
<Text
onPress={() => props.onClick('component')} >Component
</Text>
<Text
onPress={() => props.onClick('about')} >About
</Text>
</View>
)
}
Assuming you are sending an onClick prop from the parent. The the parent will know which of the children elements was clicked.
class ParentComponent extends Component {
onClickMenu = (button) => {
console.log(button);
}
render() {
return (
<View>
<MenuComponent onClick={this.onClickMenu} />
</View>
);
}
}
The onClickMenu will get the button param with the clicked button on the child component, from there you can decide what to do for each case.

React-Native Controlled Modal Freezing

Disclaimers:
Only can test on iPhone emulator atm.
React-Native 0.49
Mac OSX High Sierra
I want to create a modal which gets its props from a parent component.
As below:
const Modal = ({ showModal, closeModal }) => (
<Modal
animationType="slide"
transparent={false}
visible={showModal}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={{marginTop: 22}}>
<Text>Hello World!</Text>
<TouchableHighlight onPress={() => closeModal() }>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</Modal>
);
This is the parent example:
<View>
<Modal
showModal={this.state.showModal}
closeModal={() => this.setState({ showModal: false })}
/>
<ScrollView>
{elements.map(element => {
return (
<Card key={element.id}>
<Badge onPress={() => this.setState({ showModal: true })>
<Text>Show</Text>
</Badge>
</Card>
);
})}
</ScrollView>
</View>
When I click the show modal button the modal pops-up as expected but when I click closeModal then the modal disappears and reappears again but this time I cannot interact with it, the UI seems as if it is frozen, I have to then restart the emulator.
If I copy and paste the code straight from the React-Native docs:
https://facebook.github.io/react-native/docs/modal.html
The modal works fine. It is a self-contained component though.
Any help/advice would be much appreciated.
Regards,
Emir
After painfully rebuilding the component from scratch I see there was a unsuspected culprit:
componentWillUpdate() {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
LayoutAnimation.easeInEaseOut();
}
When I removed this code the modal worked fine but when I added it back it froze when I tried to close. This seems to be some animation conflict in iOS cant confirm for Android.
When I added a timeout of 1000ms the screen revealed a little more before it froze again.
So for now if someone has the same issue look for multiple animations being called.
Hope this helps someone, and if you have a better way of solving it please do let me know.
Regards,
Emir
This is a know issue and nothing to do with your code.
See here: https://github.com/facebook/react-native/issues/16895
Make sure that your <Modal/> is wrapped in a <View/>.
Very late, but issue still exist in latest version, And only solution i found is make different views in render method.
one for modal and one for other component.
render() {
if (showErrorModal) {
return (
<ModalError message={message} visible={showErrorModal} handleBack={this.handleBack} />
);
}
return (
<ScrollView style={{ flex: 1 }}>
<View style={{ padding: 10, paddingVertical: 20 }}>
{!active ? this.fieldlabel() : this.fieldSelect()}
</View>
// remove this one, do not use here. it will block the UI
{* <ModalError message={message} visible={showErrorModal} handleBack={this.handleBack} /> *}
</ScrollView>
);
}
}
Inside your Parent component, create a function will set showModal to false.
Parent
closeModal = () => {
this.setState({
showModal: false
});
}
Then you need to pass it down to your Modal, via props.
<Modal showModal={this.state.showModal} closeModal={this.closeModal} />
Inside of your Modal, change:
<TouchableHighlight onPress={() => this.closeModal() }>
To:
<TouchableHighlight onPress={closeModal}>
Why do u use this.closeModal()? Use the one u are getting from the props,i.e just closeModal().
<TouchableHighlight onPress={() => closeModal() }>

How to handle a touch gesture by multiple components in react native?

Using React Native 0.47 and yarn, I'm creating an introduction section in which you can swipe through multiple pages and finally get to the home page.
So we swipe pages from right to left and move like this: Slide1 -> Slide2 -> Slide3 -> Slide4 -> Home . I have a component that displays a slide page which covers all of the screen. For example, first slide looks like this:
And the code for that is :
class IntroSlide extends Component {
render () {
return (
<View style={[styles.introSlide, this.props.myStyle]}>
<View style={styles.introSlideIconView}>
<Icon name={this.props.iconName} size={SLIDE_ICON_SIZE} color="white"/>
</View>
<View style={styles.introSlideTextView}>
<Text style={styles.introSlideTextHeader}> {this.props.headerText} </Text>
<Text style={styles.introSlideText}> {this.props.text} </Text>
</View>
</View>
);
}
}
And i use it that here:
import AppIntro from 'react-native-app-intro';
// Other imports here
export default class Intro extends Component {
render() {
return (
<View style={{flex: 1}}>
<AppIntro
showSkipButton={false}
nextBtnLabel={<Text style={styles.introButton}>بعدی</Text>}
doneBtnLabel={<Text style={styles.introButton}>شروع</Text>}
>
<IntroSlide
iconName="user"
myStyle={styles.introSlide1}
headerText="DontCare"
text= "SomeTextDoesntMatter"
/>
</AppIntro>
</View>
);
}
}
I'm using react-native-app-intro for this which provides me a very cool swiper but here's the problem:
I have a small View component with an Icon (react-native-vector-icons) in it as you can see that i want to rotate as i swipe to the next page. I created a panResponder in IntroSlide and set it to top View of it. but my slide can't receive any events. i think some parent views provided by react-native-app-intro captures all the events and i can still swipe through pages but no console log appears. here's what i did:
class IntroSlide extends Component {
constructor(props) {
super(props);
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: () => console.log("Moving"),
});
this.pan = panResponder;
}
render () {
return (
<View
style={[styles.introSlide, this.props.myStyle]}
{...this.pan.panHandlers}
>
<View style={styles.introSlideIconView}>
<Icon name={this.props.iconName} size={SLIDE_ICON_SIZE} color="white"/>
</View>
<View style={styles.introSlideTextView}>
<Text style={styles.introSlideTextHeader}> {this.props.headerText} </Text>
<Text style={styles.introSlideText}> {this.props.text} </Text>
</View>
</View>
);
}
}
I did the same to the View component with flex: 1 and i had different result. i get console logs as expected but i can't swipe anymore. So how can i swipe through pages while receiving touch events in parent component ? (In other words, i want two different components to receive touch events, and I'm developing for android)

Modal doesn't work with LayoutAnimation on iPhone

This is what I'm trying to do, basically, click Open Modal button to open modal, then click Close Modal button inside modal to close it. The two pictures below are how both cases should look like.
This is my code:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
componentWillUpdate() {
if(PlatForm.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
LayoutAnimation.easeInEaseOut();
}
render() {
return (
<View style={{ ... }}>
<TouchableOpacity onPress={() => this.setState({ showModal: true })}>
<Text style={{ ... }}>
Open Modal
</Text>
</TouchableOpacity>
<Modal visible={this.state.showModal} animationType='slide'>
<View style={{ ... }}>
<TouchableOpacity onPress={() => this.setState({ showModal: false })}>
<Text style={{ color: 'white', fontSize: 20 }}>
Close Modal
</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
}
}
Everything works as expected when running on an IOS simulator, but problems rises when running on an iPhone. When press Close Modal, modal disappears for like half a second, then reopens itself again, and this time, Close Modal button won't work, I cannot re-close modal. All I can do is to re-build project. However, when I delete componentWillUpdate(), it works again, both on simulator and on iPhone
I believe this is a bug introduced in a recent version of React Native. Your best bet, for now, is to disable LayoutAnimation. Not ideal...I know.
See the discussion on Github here: https://github.com/facebook/react-native/issues/16182