So I have a homescreen that renders a modal component. I use some states for modal visibility. However, the problem is that when I navigate from the modal to another screen and then back to my home screen, the modal is still opened and I cannot figure out how to close it. I tried using useEffect but it does not do anything. Any tips?
This is home screen.
The AddButton component is a simple TouchableOpacity that onPress call the toggleModal function
const [isModalVisible, setIsModalVisible] = useState(false);
const toggleModal = () => {
setIsModalVisible(!isModalVisible);
};
useEffect(() => {
setIsModalVisible(false)
}, [navigation])
return (
<AddButton
title="ADD BOOK"
toggleModal={toggleModal}
/>
)
And the Modal Component is this:
const ModalComponent = ({navigation, isModalVisible, toggleModal, title, author, save, onNavigate }) => {
return (
<Modal isVisible={isModalVisible}>
<View style={styles.container}>
<Text>Modal</Text>
<View style={styles.footer}>
<TouchableOpacity onPress={toggleModal}>
<Text>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("AddBookScreen")}>
<Text>{save}</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
);
};
Modify this code
<TouchableOpacity onPress={() => navigation.navigate("AddBookScreen")}>
<Text>{save}</Text>
</TouchableOpacity>
to
<TouchableOpacity onPress={() => {
navigation.navigate("AddBookScreen")
toggleModal();
}}>
<Text>{save}</Text>
</TouchableOpacity>
extract onPress function and move it up to your page and pass it as onSaveClick callback to the modal component.
// on your page
const onSaveClick = () => {
navigation.navigate("AddBookScreen")
setIsModalVisible(false)
}
then in the modal
<TouchableOpacity onPress={onSaveClick}>
<Text>{save}</Text>
</TouchableOpacity>
Related
want to update a component view by using the parameter value passed from onPress function in react native.
const onItemClick = (item) => {
alert(JSON.stringify(data));
}
const renderItem = ({ index, item }) => (
<TouchableOpacity onPress={ () => {onItemClick(item)} } >
<Text> {item.name} </Text>
</TouchableOpacity>
);
Is it possible in react-native to navigate through the same route with different params? When I tried it, it keeps getting me an error saying that the params are undefined. Here is my code:
function Home({ route, navigation }){
const { newText } = route.params;
return (
<View style={styles.container}>
<Text style={{fontSize:30, paddingVertical:150, fontWeight:'bold'}}>Current Balance: {JSON.stringify(newText)} PHP</Text>
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("AddBalance")}>
<Text style={styles.btnLogin}>Add Balance</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("ViewCode")}>
<Text style={styles.btnLogin}>View Code To Pay</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("Login")}>
<Text style={styles.btnLogin}>Logout</Text>
</TouchableOpacity>
</View>
);
}
function AddBalance({ navigation }){
const[balance, newBalance] = useState(0)
const[amount, setAmount] = useState()
function addTogether(){
const Total = balance + amount;
newBalance(Total);
MyFunction();
}
return (
<View style={styles.container}>
<Text style={{fontSize:30, paddingVertical:20, fontWeight:'bold'}}>Add Balance</Text>
<TextInput style={styles.inputBox} placeholder="Enter Amount" keyboardType={'numeric'}
onChangeText={(text) => setAmount(Number.parseInt(text))}></TextInput>
<TouchableOpacity style={styles.btnLogin1} onPress ={addTogether}>
<Text style={styles.btnLogin}>Continue</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("Home", {newText:
balance})}>
<Text style={styles.btnLogin}>Back</Text>
</TouchableOpacity>
</View>
);
}
function ViewCode({ navigation }){
return (
<View style={styles.container}>
<Text style={{fontSize:30, fontWeight:'bold'}}>Your Code is:</Text>
<Text style={{fontSize:20, textAlign:'center'}}>R3fGH7X95iW</Text>
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("Home")}>
<Text style={styles.btnLogin}>Back</Text>
</TouchableOpacity>
</View>
);
}
So the error goes when I navigate to ViewCode to pay and click the back button. How can I solve this? I also created a duplicate screen for a home to manage to go back to the home screen but when I tried to navigate to the duplicate home screen, the current balance became 0 again. What I want to do is whenever the user go back to home screen the current balance will base upon the balance the users added.
The problem is in Home, you assume route.params always there (codes below)
const { newText } = route.params;
but, when you press the back button in ViewCode, it will call navigate.navigation("Home"); without any params. (codes below)
<TouchableOpacity style={styles.btnLogin1} onPress={()=>navigation.navigate("Home")}>
<Text style={styles.btnLogin}>Back</Text>
</TouchableOpacity>
This will lead to the error that you got.
The solution is to make the param optional. You can replace this code:
const { newText } = route.params;
to
const { newText } = route.params || {newText: ""};
You can replace "" with any other text as the default newText.
For saving the value globally, I recommend using Context from React. You can read more about it here
Here is the example of a Provider component you can use:
const ExampleContext = React.createContext({
setBalance: null,
balance: "",
});
const ExampleProvider = (props) => {
const [balance, setBalance] = useState("");
return (
<ExampleContext.Provider
value={{
balance: balance,
setBalance: setBalance,
}}
>
{props.children}
</ExampleContext.Provider>
);
};
and wrap your navigator with ExampleProvider
then call setBalance from the new context like this:
function AddBalance({ navigation }){
const balanceContext = React.useContext(ExampleContext);
const[amount, setAmount] = useState()
function addTogether(){
const Total = balance + amount;
balanceContext.setBalance(Total);
MyFunction();
}
and get balance from context instead from the route.params
function Home({ route, navigation }){
const balanceContext = React.useContext(ExampleContext);
const newText = balanceContext.balance;
I have a react function component, that I do not want to convert to a class component. I want it to have the functionality so that when it is pressed, the image is changed. However, currently, it is only showing one of the images (checked.png). What am I doing wrong?
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
{ check } === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Remove {} around the check
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
check === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Otherwise it is an object created everytime
Calling navigation.navigate('DrawerOpen') from inside a HOC doesn't open the drawer menu
I have written a higher order component which returns a screen with header for drawer navigator.
Header
const Header = (props)=>{
return (
<View style={{flex:1,backgroundColor:"red",justifyContent:"center"}}>
<TouchableWithoutFeedback onPress={() => {
props.navigation.navigate('DrawerOpen');
if (props.navigation.state.index === 0) {
props.navigation.navigate('DrawerOpen')
} else {
props.navigation.navigate('DrawerClose')
}
}}>
<View >
<Text style={{fontSize:20,color:"white"}}>{"Menu"}</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
}
HOC
const createCompWithHeader = (Comp)=>{
return (props)=>{
return (
<View style={{flex:1,marginTop:20}}>
<Header {...props}/>
<View style={{flex:9}}>
<Comp {...props}/>
</View>
</View>
);
}
}
Drawer Navigator
const MyApp = DrawerNavigator({
Home: {
screen: createCompWithHeader(MyHomeScreen),
},
Notifications: {
screen: createCompWithHeader(MyNotificationsScreen),
},
});
Components(Screens)
class MyHomeScreen extends Component {
static navigationOptions = {
drawerLabel: 'Home'
};
render() {
return (
<View>
<Button
onPress={()=>this.props.navigation.navigate('DrawerOpen')}
title={"Open menu"}
/>
<Button
onPress={() => this.props.navigation.navigate('Notifications')}
title="Go to notifications"
/>
</View>
);
}
}
class MyNotificationsScreen extends Component {
static navigationOptions = {
drawerLabel: 'Notifications'
};
render() {
return (
<View>
<Button
onPress={()=>this.props.navigation.navigate('DrawerOpen')}
title={"Open Menu"}
/>
<Button
onPress={() => this.props.navigation.goBack()}
title="Go home"
/></View>
);
}
}
Current Behavior
when I click on the button in header(props.navigation.navigate('DrawerOpen')), the drawer menu doesn't open
When I click on the button inside the component, menu opens
Expected Behavior
On click of the button in the header, menu should open.
How to reproduce
Expo Url : Snack
Environment
| react-navigation |1.5.8
| react-native |0.55.2
After 2.0 release, rather than opening a drawer with navigation.navigate(‘DrawerOpen’), you can now call navigation.openDrawer().
Other methods are closeDrawer() and toggleDrawer().
I've checked your snack expo and the problem lies in this snippet
if (props.navigation.state.index === 0) {
props.navigation.navigate('DrawerOpen')
} else {
props.navigation.navigate('DrawerClose')
}
The navigation does not add an index to the state, therefore it's value remains undefined
So what you are doing is , Opening and Closing the drawer yourself
Therefore remove this snippet to see it work.
onPress={() => props.navigation.navigate('DrawerOpen')}
I am asking something that is probably trivial but that has been bugging me for a while.
Lets say we have a CHILD component that renders some buttons:
const Picker = ({ data, label, visible, cancelCallback, onPressCallback }) => {
function renderRow (data) {
return data.map((el) => {
return (
<TouchableOpacity
style={listViewItemContainer}
key={el.label}
onPress={}
>
<Text style={listViewItem}> { el.label } </Text>
</TouchableOpacity>
);
});
}
return (
<Modal
visible={visible}
animationType="fade"
onRequestClose={() => {}}
transparent={true}
>
<View style={listViewContainerStyle}>
{ renderRow(data) }
</View>
</Modal>
);
};
Now when I press one of those buttons I want to make a callback to the parent BUT I want to pass some parameters like the label of the element pressed. Something like:
<TouchableOpacity
style={listViewItemContainer}
key={el.label}
onPress={onPressCallback(el.label)} // onPressCallback is a Prop passed to the child
>
<Text style={listViewItem}> { el.label } </Text>
</TouchableOpacity>
And then handle the logic in the parent component.
How can I do this?
Like this.
onPress={() => onPressCallback(el.label)}