Return useState to default afer onPress - react-native

I'm writing a customised modal for prompts on my react native application. I'd want the modal to appear on click of an image. So, I added an onPress to the image to change a state value. When the state value is true, I'd want the modal to be displayed. The state successfully becomes true. However, I'd want it to switch back to false after the modal closes.
I tried changing the values from inside the component but it throws an error since I'm importing the component, and passing values as props. Below is what I have:
const [showPrompt, setShowPrompt] = useState(false)
const show =() => {
setShowPrompt(true);
setShowPrompt(false)
console.log(showPrompt)
}
<TouchableOpacity onPress={show}>
<Image source={{uri: imageSource}} style={{height:130, width:150}}/>
<Text style={{color:'white', textAlign:'center', fontSize:16, fontFamily:fonts.nunitoRegular}}>{title}</Text>
</TouchableOpacity>
{showPrompt && <CustomPrompt
open={modalVisible}
onClose={()=> setModalVisible(!modalVisible)}
openModalText={()=>setModalVisible(true)}
/>
}

try useCallback()
setShowPrompt(true);
//the time between these 2 operations is just a Boolean value assignment,
//that is, it is only a few milliseconds and you would be able to view
//the rendering
setShowPrompt(false);
import React, {useState, useCallback} from 'react'
const show = useCallback(() => {
setShowPrompt(!showPrompt);
console.log(showPrompt)
}, [showPrompt]);
<TouchableOpacity onPress={show}>
<Image source={{uri: imageSource}} style={{height:130, width:150}}/>
<Text
style={{
color:'white',
textAlign:'center',
fontSize:16,
fontFamily:fonts.nunitoRegular}}>
{title}
</Text>
</TouchableOpacity>

Related

React-Native: Passing functions in props.navigation.navigate("XYZScreen", myFunction())

in my react-native application, there are two screens. I want to navigate from one screen to another such that if I press a button in the next screen, the first screen should be refreshed using a method doRefreshing() in the first screen.
Example:
const FirstScreen = (props) => {
const doRefreshing = () => {
...
};
return (
<View>
<TouchableOpacity
onPress={()=>props.navigation.navigate("secondScreen",doRefreshing())}
>
</TouchableOpacity>
</View>
);
}
The example code of second screen:
const SecondScreen = (props) => {
const doRefreshingInFirstScreen = () => {
/** What should I do here? */
};
return (
<View>
<Button
onPress={()=>doRefreshingInFirstScreen()}
>
</Button>
</View>
);
}
Can someone please tell me how shall I refresh the first screen when the button in second screen is pressed and call goes to doRefreshingInFirstScreen()? Or what else I can do to refresh the first screen upon clicking the button in the second screen? Will be really grateful for this help. Thanks in advance.
Did you mean this?
Snack
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', {onRefresh: () => console.log('refreshing...')})}
/>
</View>
);
}
function DetailsScreen(props) {
const { route } = props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button onPress={route.params.onRefresh} title="Refresh" />
</View>
);
}
React Native re-renders the screen whenever there is state change but this re-rendering will not happen if the screen is not being displayed. As for your scenario, I will suggest using redux to store the values that need to be refreshed from the second screen. Now whenever you go back to the first screen, then if you are reading value from the redux store, it will read the new values for all the variables that needed to be refreshed from the second screen, thus you will have the new values instead of the old ones.

How do I popup modal in the beginning? React-native navigation

I am using react navigation v5 to create my modal.
I want my modal to show in the beginning of 50% height, and when I press a button I want my modal to have 100% height. And toggle 50% to 100% to 50% back and forth.
However, I am unsure how to do this.
My code looks something like this.
Navigation
render(){
return (
<NavigationContainer>
<RootStack.Navigator mode="modal" headerMode = "none">
<RootStack.Screen name="home" component = {Home} />
<RootStack.Screen name="modala" component={ModalA}
options={{cardStyle: {backgroundColor: "transparent"}}}/>
</RootStack.Navigator>
</NavigationContainer>
);
}
As you can see I want my modalA to overlap Home component in the beginning as 50% width and if i press a button inside ModalA, I want my modal to be 100%. Is this possible?
Yes, its possible. To show the modal on the initial screen you must create this Modal Component on that screen
e.g
import React, { Component, useState } from 'react';
import {View, Text} from 'react-native';
import Modal from "react-native-modal";
const Home = (props) => {
const [height, setHeight] = useState('50%');
return (
<View>
<Modal isVisible={true}>
<View style={{ height, width: "80%", justifyContent: 'center', alignItems: 'center'}}>
<Text>I am the modal content!</Text>
<TouchableOpacity onPress={() => setHeight("50%")}>
<Text>Set 50% Height</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setHeight("100%")}>
<Text>Set 100% Height</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
}

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

How to disable highlighting effect of TouchableOpacity when scrolling?

<TouchableOpacity style={{ flex: 1 }} >
<ImageBackground
source={require('../../images/home.jpg')}>
<View style={styles.item} collapsable={false}>
<H3>{contentData[i].name}</H3>
<Text>{contentData[i].description}</Text>
</View>
</ImageBackground>
</TouchableOpacity>
I have a list of TouchableOpacity inside a ScrollView. I want to disable highlighting effect of TouchableOpacity. When scrolling I want to highlight only when onPress event is triggered. Because it may confuse the user that it is pressed.
Simply pass activeOpactity prop with value 1.
<TouchableOpacity activeOpacity={1}>....</TouchableOpacity>
Make sure you import TouchableOpacity from "react-native" not from "react-native-gesture-handler".
Try setting the activeOpacity prop on the TouchableOpacity to 1 when scrolling. Use default settings when the user stops scrolling.
https://facebook.github.io/react-native/docs/touchableopacity#activeopacity
You can try changing param delayPressIn. Look doc.
<TouchableOpacity delayPressIn={150} >
{children}
</TouchableOpacity>
You can make use of onScrollBeginDrag and onScrollEndDrag props.
state = {
scrollBegin: false
}
scrollStart = () => this.setState({scrollBegin: true})
scrollEnd = () => this.setState({scrollBegin: false})
<ScrollView onScrollBeginDrag={this.scrollStart} onScrollEndDrag={this.scrollEnd}>
... Other stuff
</ScrollView>
and set activeOpacity={1} for TouchableOpacity when this.state.scrollBegin=true
You could try replace TouchOpacity with RectButton in 'react-native-gesture-handler'. And don't forget to replace the ScrollView import from 'react-native' to 'react-native-gesture-handler'.
I found this solution in here.
It just said:
provides native and platform default interaction for buttons that are placed in a scrollable container (in which case the interaction is slightly delayed to prevent button from highlighting when you fling)
We implemeted a custom Touchable component using TouchableOpacity as click element and a wrapper View handling the opacity of the children elements.
By setting activeOpacity={1} to default and the pressed state to true when clicking, we can delay the rest of the onPress functionality by a unnoticeable 100ms to display an opacity shift when clicking. Which is shipped to the wrapper View. The View is wrapped inside the touchable instead of outside to better preserve styling.
We also added cleanup when component is unmounted in useEffect()
import React, { useEffect, useState } from "react";
import { View, TouchableOpacity } from "react-native";
const Touchable = (props) => {
const { children, onPress } = props;
const [pressed, setPressed] = useState(false);
useEffect(() => {
return setPressed(false);
}, []);
return (
<TouchableOpacity
{...props}
activeOpacity={1}
onPress={() => {
setPressed(true);
setTimeout(() => {
setPressed(false);
onPress();
}, 100);
}}
>
<View style={{opacity: pressed ? 0.8 : 1}}>
{children}
</View>
</TouchableOpacity>
);
};
export default Touchable;
I had the same issue, so I wrote this class that I use instead of <TouchableOpacity> in my code:
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native';
import TimerMixin from 'react-timer-mixin';
class TouchableOpacityScrollable extends Component {
_onPress() {
const { onPress } = this.props;
// Looking in the TouchableOpacity source code we see that
// the touch Opacity is 150, and that it comes back in 250 milliseconds.
// #see https://github.com/facebook/react-native/blob/c416b40542ece64e26fb2298485ae42eeebc352a/Libraries/Components/Touchable/TouchableOpacity.js
this.refs.touchableOpacity.setOpacityTo(0.2, 150);
TimerMixin.setTimeout(() => {
onPress();
this.refs.touchableOpacity.setOpacityTo(1, 250);
}, 150);
}
render() {
const { style, children } = this.props;
return (
<TouchableOpacity
ref="touchableOpacity"
style={style}
activeOpacity={1.0}
onPress={() => this._onPress()}
>
{children}
</TouchableOpacity>
);
}
}
export default TouchableOpacityScrollable;
You will have to install react-timer-mixin to prevent possible crashes.
Enjoy!
after upgrading RN version to 0.63.2 TouchableOpacity is working like it should, during scrolling, hover effect doesn't appears

React-Native = Invariant Violation: Maximum update depth exceeded

I have this error, and i didn't have it before :
here is the image of the error
Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
This error is located at:
in Connect (at LoginForm.js:75)
render() {
const { inputStyle, containerStylePass, containerStyleIdent, barStyle, textInputStyle } = styles;
return (
<View>
<View>{/* all the password form*/}
<View style={containerStylePass}>
icon
<Text style={inputStyle}>Mot de passe</Text>
</View>
<TextInput
secureTextEntry
autoCorrect={false}
style={textInputStyle}
/>
<View style={barStyle} />
</View>
<View>
<Connect />
</View>
</View>
I don't know why is an error, can anyone help?
here is my code :
import React, { Component } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
class Connect extends Component {
render() {
return (
<TouchableOpacity onPress={this.setState({ butPressed: true })}>
<LinearGradient
colors={['#56EDFF', '#42F4A0']}
start={{ x: 0.0, y: 1.0 }} end={{ x: 1.0, y: 1.0 }}
>
<Text style={textStyle}>
Se connecter
</Text>;
</LinearGradient>
</TouchableOpacity>
);
}
}
try:
<TouchableOpacity onPress={() => this.setState({ butPressed: true })}>
instead of
<TouchableOpacity onPress={this.setState({ butPressed: true })}>
Assigning {this.setState} to onPress without arrow function cause to render over and over again because setState casing the component to render again and then it comes again to the assignment of onPress = {}.
Using arrow function instead causing to assign a function so the setState is actually doesn't happen until the function is activated. (only when onPress is activated)
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
If you are using Expo, restart Expo (terminate and open again). I don't know but it worked for me.