React Native Modal transparent with backgroundColor black opacity 0.5 become darker between scene - background

RN 0.57,
React router flux 4
I have loader component to display loading message in modal on every scene.
I set the modal transparent and for modal content I set the backgroundColor: 'rgba(0, 0, 0, 0.5)'
This is the component code:
<Modal
transparent
visible={this.props.visible}
onRequestClose={() => {
console.log('modal closed')
}}
>
<View style={styles.modalBackground}>
.....
</View>
</Modal>
This is the stylesheet:
modalBackground: {
flex: 1,
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'space-around',
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
This is where loader component to display:
<View style={{flex:1}}>
<Loader
visible={this.props.global.isLoading}
animating={this.props.global.isLoading}
/>
......
</View>
I change the state dispatch(setIsloading(true)) before HTTP REQ and dispatch(setIsloading(false)) after it return JSON from server, then it change to new Scene.
The problem is first visible modal is at correct color and transparent, but on the next scene, the color become darker and darker, it's like the component display multiple times at the next scene and triple after that.
Any idea What cause this problem?

As these tasks are async you have to make sure that the modal is dismissed before you move to the next scene, in your case you are moving to next scene before this modal is dismissed and once you are on next scene all the async subscriptions are removed hence your modal is not dismissing.
What I do for loading modals is that I do it by refs
In my loading component
export default class Loader extends Component{
hide = () => {
this.setState=({visible:false})
}
show = () => {
this.setState=({visible:true})
}
render(){
return(
<Modal isVisible={this.state.visible}>
....
</Modal>
)
}
}
where you want to show Loader
import Loader from 'path/to/loader'
<Loader ref={r=>this.loader = r}/>
and in your methods when you want to show loader
this.loader.show()
and hide it by calling
this.loader.hide()

Related

How to display a button at the bottom of a Webview in react-native?

Inside my component (PrivacyPolicy.js), i have a header view, a webview, and a footer view. the webview, depending on the size, gets scrollable. my issue is that the footer view is displayed at the bottom of the screen like if its style was "position: 'absolute'" so it keeps displayed while scrolling. I need to have it after all webview is displayed.
<View style={styles.main_container}>
<View style={styles.header_container}>
...
</View>
<WebView originWhitelist={['*']} source={{ html: privacyPolicyContent }}/>
<View style={styles.footer_container}>
<CheckBox
disabled={false}
value={this.state.isChecked}
onValueChange={(newValue) => this.setState({
isChecked: newValue
})}
style={styles.checkbox}
tintColors={{ true: '#157dfa' }}
/>
<Text style={styles.checkbox_text}>I have read and accept the Privacy Polic</Text>
</View>
</View>
My styles:
const styles = StyleSheet.create({
main_container: {
flex: 1,
paddingHorizontal:'5%'
},
header_container: {
height: scale(90),
flexDirection: 'row',
marginLeft: 10
},
checkbox_container: {
flexDirection: 'row'
},
checkbox: {
marginLeft: -5,
},
checkbox_text: {
marginTop: 8,
fontSize: 10
}
})
I can see few suggestions:
Since your button is a React Native Button => You can show/hide based on the scrollY positions. For that, you need to communicate over the Bridge to dispatch an event accordingly.
As an alternative solution => You can create the button on the Webview its self to have the same functionality.

Change the backgroundcolor of Touchable / Pressable Item after pressing

I'm currently working on an app prototype with react native. There's a lot out there on how to change the color of a component, here Touchable or Pressable, when pressing it (=> onPress).
But how do i change the backgroundcolor of such a component permanently after clicking – with onPressOut?.
Example:
simple "Click me" component that has a green background by default. If clicked once, it should change to a red background. Clicked once again, it should go back to green (and so on).
Can you help me with this?
You need to control it using the state of component.
I did a live demo for you:
https://codesandbox.io/s/silent-sea-5331l?file=/src/App.js
import React, { useState } from "react";
import { View, TouchableOpacity } from "react-native";
const App = props => {
const [selected, setSelected] = useState(false);
return (
<View style={{ width: "30%", alignItems: "center", marginTop: 20 }}>
<TouchableOpacity
onPress={() => setSelected(!selected)}
style={{ backgroundColor: selected ? "red" : "transparent" }}
>
Press me
</TouchableOpacity>
</View>
);
};
export default App;

Create a reusable React Native Modal Component

I'm going back to basics with React Native, as I feel overwhelmed. I have been looking for an implementation of a reusable modal component. I'm looking for examples of a reusable Modal component in RN? Thanks in advance
You can find many examples of this on StackOverflow. Still, if you need example I can help you with one example. You have mentioned modal component in your question, right?
Your component will look like this with props. let the name be ModalComponent for this file.
render() {
const { isVisible, message, textValue } = this.props;
return (
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
<Text>textValue</Text>
<Text>message</Text>
</View>
</Modal>
);
}
so now in your js file you need to import this modalComponent and after that, you need to write as
<ModalComponent
isVisible={true}
textValue={'hi there'}
message={'trying to make a basic component modal'}/>
Hope this will help for you
EDIT:
Create seperate components that you want to render inside modal. for Ex: component1.js, component2.js, component3.js with props
component1.js:
render(){
const { textVal, message } = this.props
return (
<View>
<Text>{textVal}</Text>
<Text>{message}</Text>
</View>
)
}
now in ModalComponent
render() {
const { first, second, third, isVisible, component1Text, component1Message } = this.props;
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
{first && <component1
textValue= component1Text
message= component1Message />}
{second && <Component2 />}
{third && <Component2 />}
</View>
</Modal>
In this way, you can achieve it within the single modal.
You will make a component like this giving the parent component all the liberty to change it through props.
render() {
const { isVisible, message, textValue, animationType, backDropColor, style, onModalHide, children } = this.props;
return (
<Modal
animationType= {animationType || 'slide'}
transparent={transparent || false}
isVisible={isVisible || false}
backdropColor={backdropColor || "white"}
style={[modalStyle, style]}
onModalHide={onModalHide}>
{children}
</Modal>
);
}
Then in your parent component, you need to import this component like this:
import ModalComponent from '../ModalComponent'; //path to your component
<ModalComponent isVisible={true}>
<View>
//any view you want to be rendered in the modal
</View>
</ModalComponent>
I had a lot of troubles using react-native modal, sometimes i started the app and could not close it even when i set the isVisible prop to false, it is even worst on IOs, i did a research and these packages are not being maintained properly.
You will save a lot of time by using a top-level navigator like is recommended in the modal docs: https://facebook.github.io/react-native/docs/modal.
I tried https://github.com/react-native-community/react-native-modal but had the same problems because its an extension of the original react-native modal.
I suggest you to use the react-navigation modal as described here: https://reactnavigation.org/docs/en/modal.html#docsNav
You can refer the following code to write Modal component once and use multiple times.
Write once:
import React, { Component } from 'react';
import { View, Text, Button, Modal, ScrollView, } from 'react-native';
export class MyOwnModal extends Component {
constructor(props) {
super(props);
this.state = {
}
render() {
return(
<Modal
key={this.props.modalKey}
transparent={this.props.istransparent !== undefined ? true : false}
visible={this.props.visible}
onRequestClose={this.props.onRequestClose}>
<View style={{
//your styles for modal here. Example:
marginHorizontal: width(10), marginVertical: '30%',
height: '40%', borderColor: 'rgba(0,0,0,0.38)', padding: 5,
alignItems: 'center',
backgroundColor: '#fff', elevation: 5, shadowRadius: 20, shadowOffset: { width: 3, height: 3 }
}}>
<ScrollView contentContainerStyle={{ flex: 1 }}>
{this.props.children}
</ScrollView>
</View>
</Modal>
);
}
}
Now,
You can call your Modal like following example: (By doing this, you avoid re-writing the Modal and its outer styles everytime!)
Example
<MyOwnModal modalKey={"01"} visible={true} onRequestClose={() =>
this.anyFunction()} istransparent = {true}>
<View>
// create your own view here!
</View>
</MyOwnModal>
Note: If you are in using different files don't forget to import , and also you can pass the styles as props.
(You can create/customise props too based on your requirement)
Hope this saves your time.
Happy coding!
I am a contributor of react-native-use-modal.
This is an example of creating a reusable modal in a general way and using react-native-use-modal: https://github.com/zeallat/creating-reusable-react-native-alert-modal-examples
With react-native-use-modal, you can make reusable modal more easily.
This is a comparison article with the general method: https://zeallat94.medium.com/creating-a-reusable-reactnative-alert-modal-db5cbe7e5c2b

Navigating to the first component in a stack navigator with params doesn't work

I have a component within a StackNavigator that I always want to pass params to. This doesn't seem to work if my component is the first component loaded in a StackNavigator. Here's some code that illustrates the problem.
I start in component A.
I press the button to navigate to component B.
In the logs you can see that component B is loaded twice. The first time it's loaded without params.
Android SDK built for x86: rendering with: undefined
Android SDK built for x86: rendering with: ►{data:"from A"}
Also if you click the device back button it goes back to the screen with undefined params.
So there are two issues:
I only navigated once, but I have to go back twice to get back to where I was.
My component is loaded with no params even though I navigated with params.
Here's the code running in an expo snack: https://snack.expo.io/#bdzim/navigate-to-tab-from-parent
class A extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="This is A. Press to go to B"
onPress={() => this.props.navigation.navigate(
'B', { data: 'from A' }
)}
/>
</View>
);
}
}
class B extends React.Component {
render() {
console.log('rendering with: ', this.props.navigation.state.params)
let params = JSON.stringify(this.props.navigation.state.params)
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>
params: {params}
</Text>
<Button
title="This is.. B! Press to go to A"
onPress={() => this.props.navigation.navigate('A')}
/>
</View>
);
}
}
const Stacks = StackNavigator({ B })
const Tabs = TabNavigator({ A, Stacks });

How do I prevent a user clicking through an overlay?

I have an overlay positioned absolute, it has backgroundColor and it covers the whole screen. It's overlaying several button components which I can still click on through the overlay.
How do I prevent this behavior? I want to swallow all click events landing on the overlay first.
Code:
// Overlay
export default class Overlay extends Component {
render() {
return (
<View style={styles.wrapper} />
);
}
}
const styles = StyleSheet.create({
wrapper: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: "black",
opacity: 0.7
}
});
// Container
export default class Container extends Component {
render() {
return (
<View>
<Overlay />
<Button onPress={() => this.doSomething()}>
<Text>Hello</Text>
</Button>
</View>
);
}
}
Write the absolute position component after other components to render it over the other components.
export default class Container extends Component {
render() {
return (
<View>
<Button onPress={() => this.doSomething()} title="Hello" />
<Overlay /> // provide appropriate height and width to the overlay styles if needed...
</View>
);
}
}
Solution 1- You can try to use Modal component from react-native
Solution 2- Wrap TouchableWithoutFeedback having blank onPress around your overlay. Don't forget to give full height and width to TouchableWithoutFeedback
something like
<TouchableWithoutFeedback>
<Overlay/>
<TouchableWithoutFeedback/>