React Hooks: saving state from one modal to another - javascript

I am trying to make a Todo List creater with 2 nested modals. Each one of the modals passes a state to the Todo Component which is then published in the App.js. The App.js has an initial hook state for showModal1 as false, but it changes to true when I press the button to open the Modal1.
The first modal (Modal1) is suposed to pass the task state to the second modal (Modal2).
import React,{useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity, TextInput, Modal} from 'react-native';
import {AntDesign} from '#expo/vector-icons';
import Modal2 from './Modal2';
function Modal1(props) {
const [showModal2, setShowModal2] = useState(false)
const [task, setTask] = useState('')
const toggleShowModal2 = (task) => {
setShowModal2(!showModal2);
}
function handleTask(value) {
setTask(value)
}
return(
<View behavior='padding'>
<Modal animationType='slide' visible={showModal2} onRequestClose={toggleShowModal2}>
<Modal2 closeModal={toggleShowModal2}/>
</Modal>
<TouchableOpacity onPress={props.closeModal}>
<AntDesign name='close' size={24} color='black'/>
</TouchableOpacity>
<View>
<Text>First Step</Text>
<Text>Task</Text>
<TextInput placeholder='What is the task?' value={task} onChangeText={handleTask}/>
</View>
<View>
<TouchableOpacity onPress={() => toggleShowModal2(task)}>
<AntDesign name='arrowright' size={40} color='black'/>
</TouchableOpacity>
</View>
</View>
);
};
And the second modal should be able to pass the time state and then create the Todo with the state values.
import React, {useState, useEffect} from 'react';
import { StyleSheet, Text, View, KeyboardAvoidingView, TouchableOpacity, TextInput, ScrollView} from 'react-native';
import {AntDesign} from '#expo/vector-icons';
function Modal2(props) {
const [time, setTime] = useState('')
function handleTime(value) {
setTime(value)
}
function createTodo(){
const list = {task, time};
setTask('')
setTime('')
props.addList(list);
props.closeModal();
}
return(
<View behavior='padding'>
<TouchableOpacity onPress={props.closeModal}>
<AntDesign name='close' size={24} color='black'/>
</TouchableOpacity>
<View>
<Text>Second Step</Text>
<Text>Time</Text>
<TextInput placeholder='When are you going to do it?' value={time} onChangeText={handleTime}/>
</View>
<View>
<TouchableOpacity onPress={createTodo}>
<AntDesign name='arrowright' size={40} color='black'/>
</TouchableOpacity>
</View>
</View>
);
};
However, whenever I try to publish the Todo, the following error pops up:
ReferenceError: Can't find variable: task
I am new to React (and React Hooks) and I don't know if it is possible to save state from one function to another in a similar way.

In order to access variables defined in another component, you need to pass them as props,
so you can pass the variables as props in Modal1
...
<Modal2 closeModal={toggleShowModal2} task={task} setTask={setTask} />
...
and access them in Modal2 like so,
function createTodo(){
const { task, setTask } = props;
const list = { task, time };
setTask('')
setTime('')
props.addList(list);
props.closeModal();
}

Related

TypeError: setEmail is not a function. (In 'setEmail(input)', 'setEmail' is undefined) (unable to use hooks with context api)

App.js
const[getScreen, showScreen] = useState(false);
const[getEmail, setEmail] = useState("");
<LoginScreen/>
<LoginContexts.Provider value={{getEmail,setEmail,getScreen,showScreen}}>
{showScreen?<Screen2/>:<LoginScreen/> }
</LoginContexts.Provider>
Login Screen
import
{ StyleSheet, Text, TextInput, View, Button
} from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { useState, useContext } from 'react';
import { LoginContexts } from '../Contexts/LoginContexts'
function LoginScreen (){
const {getEmail,setEmail,getScreen,showScreen} = useContext(LoginContexts);
return(
<View style={styles.container}>
<Text style={styles.loginText}>Login</Text>
<View style={styles.textInput}>
<TextInput placeholder='Please enter your email'
defaultValue={getEmail}
onChange={(input) =>{setEmail(input)}}/>
</View>
**^^^^^^^^^^^^^^^^^^^^^Problem lies here^^^^^^^^^^^^^^^**
<View style={styles.space}/>
<View style={styles.space}/>
<View>
<Button style={styles.loginBTN} title="Login"
onPress={() => {showScreen(true)}} />
</View>
</View>
);
}
**Context API **
import { createContext} from "react";
export const LoginContexts = createContext({});
Screen 2
const {getEmail} = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginContexts.Consumer>
{(fname)=> {
<Text>{fname}</Text>;
}}
</LoginContexts.Consumer>
When I am entering a text then program is giving me an error that setEmail is not a function.
I have taken out setEmail from useContext. My aim is to display text taken from user to another screen on button click
The main purpose of Context API is to avoid prop drilling and pass data to descending components in the vDOM.
If you want to pass asynchronous callbacks to change the data in your HOC, you’re effectively updating the state of your app, which is asking for quite a lot.
The resolution is to combine your Context with a reducer via useReducer hook.
Potential work around for current implementation is to remove the ternary from HOC:
replace {showScreen?<Screen2/>:<LoginScreen/> } with { children } because when your showScreen's state is true you lose the reference to your callbacks.
Pass it as array
<LoginContexts.Provider value={[getEmail,setEmail,getScreen,showScreen]}>
You need to change the order
const {getEmail,setEmail,getScreen,showScreen} = useContext(LoginContexts);

onPress event is not working with custom button in my React Native code?

I'm working on a React Native project as a beginner. I'm creating a custom button called "RoundedButton" using touchableOpacity. But after creating this button when I use this and try to trigger an event using onPress like <RoundedButton onPress = {some function}/> it does not work.
Here is my code:
my RoundedButton.js code:
import React from 'react';
import {TouchableOpacity, Text, StyleSheet} from 'react-native';
import {colors} from '../utils/colors';
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
...props
}) => {
return (
<TouchableOpacity style ={[styles(size).radius, style]}>
<Text style = {[styles(size).text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
)
};
my Timer.js code where I uses the above custom button:
import React, {useState} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {colors} from '../../utils/colors';
import {fontSizes, spacing} from '../../utils/sizes'
import {Countdown} from '../../components/Countdown';
import {RoundedButton} from '../../components/RoundedButton';
export const Timer = ({focusSubject}) => {
const [isStarted, setIsStarted] = useState("false");
const onPress = () => setIsStarted("true");
return (
<View style={styles.container}>
<View style={styles.countdown}>
<Countdown isPaused={!isStarted}/>
</View>
<View>
<Text style={styles.title}>Focusing on: </Text>
<Text style={styles.task}>{focusSubject}</Text>
</View>
<RoundedButton title="Start" size={50} onPress={onPress}/>
<Text style={{color: 'white'}}>{isStarted ? "True" : "False"}</Text>
</View>
)
};
I have not included styles code to make it simpler to find the issue. I want to know the correct method to use onPress functionality in React Native. Please help as soon as possible. No error is shown, App is working but the pressing event is not working, so I am not able to find out the actual issue.
This is because you haven't passed onPress to TouchableOpacity. Just pass the prop and it should work just fine.
<TouchableOpacity style ={[styles(size).radius, style]} onPress={props.onPress}>
<Text style = {[styles(size).text, textStyle]}>{props.title}</Text>
</TouchableOpacity>

How would I pass data from on screen to another in React Native?

I am having trouble trying to pass some data to another screen. Currently using the useState hook and I would like to use the value on the next screen.
NumScreen.JS:
import React, { useState } from "react";
import { Text, TextInput, Button, View, StyleSheet, TouchableOpacity } from "react-native";
function NumScreen({ navigation }) {
const [num, setNum] = useState("0");
const pressHandler1 = () => {
navigation.navigate("CalcScreen", { num });
}
const Separator = () => (
<View style={styles.separator} />
);
return (
<View style={styles.container}>
<Text>Enter Number of People:</Text>
<TextInput
returnKeyType="done"
keyboardType="numeric"
style={styles.input}
placeholder="e.g. 3"
onChangeText={(val) => setNum(val)}
/>
<Text> Number: {num} </Text>
<Separator />
<TouchableOpacity onPress={pressHandler1}>
<Text style={styles.button}>Submit</Text>
</TouchableOpacity>
</View>
);
}
export default NumScreen;
I want to get the num value to the next screen, I tried sending the value using navigation.navigate then tried to use getParam but this did not work.
import React, { useState } from 'react';
import { Text, TextInput, Button, View, StyleSheet, TouchableOpacity } from "react-native";
function CalcScreen({ navigation }) {
const [tot, setTot] = useState("0");
const ppl = navigation.getParam(num);
const Separator = () => (
<View style={styles.separator} />
);
return (
<View style={styles.container}>
<Text> num: {ppl} </Text>
<Text>Enter Total Amount:</Text>
<TextInput
returnKeyType="done"
keyboardType="numeric"
style={styles.input}
placeholder="e.g. 5.50"
onChangeText={(val1) => setTot(val1)}
/>
<Text> Total: {tot} </Text>
</View>
);
}
export default CalcScreen;
Any help would be much appreciated!
When the routing between Screens happens, then you can get the num value in the CalcScreen component by the following code:
function CalcScreen({ navigation, route }) {
const { num } = route.params;
Pass params to a route by putting them in an object as a second parameter to the navigation.navigate function from First Screen. Using :
this.props.navigation.navigate('RouteName', { /* params go here */ })
Read the params in your Second screen. Using:
this.props.navigation.getParam(paramName, defaultValue)
here is the complete working example, link
Using props
We use props to pass data from one class to another or from one page to another in react native.
https://reactnative.dev/docs/props

Can't pass value between two components in react native

So I am following Academind tutorial on youtube on react native but i can't seem to get to work the passing of values between tow components.
In startgamescreen on this line onPress={()=>props.onGame(selNumber)} I seem to follow him and pass selNumber but the error I get is onGame is not a function and undefined
and in App.js
const gameHandler=(selNum)=>{
setUserNumber(selNum);
};
let content=<StartGameScreen onGame={gameHandler} />;
Here I seem to exactly follow him and pass the gamehandler but I still don't get what went wrong. Please help me I have tried to debug this code for a while now.
StartGameScreen.js
import React, {useState} from 'react';
import { View, StyleSheet, Alert ,Text, Button, TouchableWithoutFeedback,Keyboard} from 'react-native';
import NumberComponents from '../components/NumberComponent';
import Card from '../components/Card';
import Colors from '../constants/colors';
import Input from '../components/Input';
const StartGameScreen = props => {
const [enteredValue,setEnteredValue]=useState('');
const [confirmed,setConfirmed]=useState(false);
const [selNumber,setSelNumber]=useState('');
const numberHandler =textinput=>{
setEnteredValue(textinput.replace(/[^0-9]/g,''));
};
let confirmedOutput;
if(confirmed){
confirmedOutput=<Card style={styles.confirm}>
<Text>You Selected:</Text>
<NumberComponents children={selNumber}/>
<Button title='Start Game' onPress={()=>props.onGame(selNumber)}/>
</Card>
}
const resetInputHandler=()=>{
setEnteredValue('');
setConfirmed(false);
};
const confirmButtonHandler=()=>{
const chosenNum=parseInt(enteredValue);
if(isNaN(chosenNum) || chosenNum<=0 || chosenNum>99){
Alert.alert(
'InValid Number',
'Please Enter Valid Number Between 1 and 99',
[{text:'Okay',style:'destructive',onPress:resetInputHandler}]
)
return;
}
setConfirmed(true);
setEnteredValue('');
setSelNumber(chosenNum);
Keyboard.dismiss();
};
return (
<TouchableWithoutFeedback onPress={()=>{Keyboard.dismiss();}}>
<View style={styles.screen}>
<Text style={styles.title}> Start A New Game </Text>
<Card style={styles.inputContainer}>
<Text style={{ color: 'green', fontSize: 15 }}>Choose A Number</Text>
<Input
keyboardType='number-pad'
maxLength={2}
style={styles.input}
blurOnSubmit
onChangeText={numberHandler}
value={enteredValue}
/>
<View style={styles.buttonContainer}>
<View style={styles.button}>
<Button
title='Reset'
color={Colors.accent}
onPress={resetInputHandler} />
</View>
<View style={styles.button}>
<Button
title='Confirm'
onPress={confirmButtonHandler}
color='orange' />
</View>
</View>
</Card>
{confirmedOutput}
</View>
</TouchableWithoutFeedback>
);
};
in App.js
import React,{ useState } from 'react';
import { StyleSheet, View } from 'react-native';
import Header from './components/Header';
import StartGameScreen from './screens/StartGameScreen';
import GameScreen from './screens/StartGameScreen';
export default function App() {
const [userNumber,setUserNumber]=useState();
const gameHandler=(selNum)=>{
setUserNumber(selNum);
};
let content=<StartGameScreen onGame={gameHandler} />;
if(userNumber){
content=<GameScreen userChoice={userNumber}/>;
}
return (
<View style={styles.screen}>
<Header title="Guess A Number"/>
{content}
</View>
);
}
const styles = StyleSheet.create({
screen:{
flex:1,
}
});
You need to add onGame={gameHandler} into this component:
if (userNumber) {
content = <GameScreen onGame={gameHandler} userChoice={userNumber} />;
}
Since GameScreen and StartGameScreen are the same components,
import StartGameScreen from './screens/StartGameScreen';
import GameScreen from './screens/StartGameScreen';
in the second case when userNumber is not false the GameScreen component was called without the onGame props.
See the working stripped down version in codesandbox:
https://codesandbox.io/s/affectionate-wright-3bccd
Hello if there are not parent-child relation between your components,you can use Event like DeviceEventEmitter.
Here is a good link for example: [https://callstack.com/blog/sending-events-to-javascript-from-your-native-module-in-react-native/][1]

TouchableOpacity OnPress is not working

I am trying to call a function which is passed using props when onPress is clicked. Below is my custom component.
DrawerItem.js
import React from 'react';
import {
View,
Text,
StyleSheet,
Image,
TouchableOpacity
} from 'react-native';
import FastImage from 'react-native-fast-image';
const DrawerItem = (props) => (
<View style={styles.drawerItemContainer}>
<TouchableOpacity onPress={props.onPress}>
<FastImage source={props.icon} style={styles.icon} />
</TouchableOpacity>
<TouchableOpacity onPress={props.onPress}>
<Text style={styles.drawerItemText} >{props.title}</Text>
</TouchableOpacity>
</View>
);
export default DrawerItem;
Below is my custom component where I am using DrawerItem Component:
SideMenu.js:
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {NavigationActions, SafeAreaView} from 'react-navigation';
import {ScrollView, Text, View, StyleSheet,ImageBackground, StatusBar, TouchableOpacity} from 'react-native';
import FastImage from 'react-native-fast-image';
import DrawerItem from './DrawerItem';
class SideMenu extends Component {
//This is the function which is not being called
navigateToScreen = (route) => () => {
console.log('inside navigate screen');
const navigateAction = NavigationActions.navigate({
routeName: route
});
this.props.navigation.dispatch(navigateAction);
}
render () {
return (
<View style={styles.container}>
<ImageBackground source={require('../../resources/images/drawer_background.png')} style={{width: '100%', height: '100%'}}>
<View style={styles.drawer}>
<View style={styles.header}>
<FastImage style={styles.profilePicture} source={{uri: 'https://assets.entrepreneur.com/content/3x2/1300/20150406145944-dos-donts-taking-perfect-linkedin-profile-picture-selfie-mobile-camera-2.jpeg'}}/>
<View style={styles.headerDetails}>
<Text style={styles.displayName}>Jen William</Text>
<Text style={styles.email}>jen#williams.com</Text>
</View>
</View>
<View style={styles.drawerBody}>
//Below is how I am pasing navigateToScreen function
<DrawerItem onPress={() => this.navigateToScreen('Profile')} icon={require('../../resources/images/myprofile_icon.png')} title='My Profile'/>
<DrawerItem icon={require('../../resources/images/cardrequest_icon.png')} title='Card Requests'/>
<DrawerItem icon={require('../../resources/images/search_icon.png')} title='Search'/>
<DrawerItem icon={require('../../resources/images/add_icon.png')} title='My Cards'/>
<DrawerItem icon={require('../../resources/images/signout_icon.png')} title='Sign Out'/>
</View>
</View>
</ImageBackground>
</View>
);
}
}
SideMenu.propTypes = {
navigation: PropTypes.object
};
export default SideMenu;
Note: The props are being passed for sure as other values being passed
are accessible
Can someone please tell me what I am doing wrong?
It's probably a binding issue.
Instead of
onPress={() => this.navigateToScreen('Profile')}
Try:
onPress={this.navigateToScreen('Profile')}
Check your function :
navigateToScreen = (route) => () => {
console.log('inside navigate screen');
const navigateAction = NavigationActions.navigate({
routeName: route
});
this.props.navigation.dispatch(navigateAction);
}
It should be like this :
navigateToScreen = (route) => {
console.log('inside navigate screen');
const navigateAction = NavigationActions.navigate({
routeName: route
});
this.props.navigation.dispatch(navigateAction);
}
There should not be extra brackets
Well! This may be funny. But believe me. I've experienced the same error and my code is simply as below,
<TouchableOpacity onPress = {() => console.log('Hi')}>
<Text> Click Me! </Text>
</TouchableOpacity>
After 15 minutes of time, I just existed from my console to restart my app. On pressing my npm console just thrown all of console logs.
Yah, sometimes npm console on my windows machine goes crazy to make me believe problem with my code.
Depending on how you are running your code, this might not work. use onClick
Try this instead
<TouchableOpacity onClick={...}>
...
</TouchableOpacity>

Categories

Resources