React-Native: Formik ref fails to get value - javascript

I have a dummy Login code with formik form in react-native
import React, { Component } from "react";
import {
TextInput,
Text,
Alert,
Image,
View,
TouchableOpacity,
SafeAreaView,
ScrollView
} from "react-native";
import styles from "./Styles/LoginStylesheet";
import { KeyboardAccessoryNavigation } from "react-native-keyboard-accessory";
import { Formik } from "formik";
import schemaObject, { initialValues, refs } from "./Validations/LoginValidations";
export default class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
activeInputIndex: 0
};
}
handleFocus = index => () => {
this.setState({
activeInputIndex: index
});
};
handleFocusNext = () => {
if (this.state.activeInputIndex + 1 >= refs.length) {
return;
}
refs[this.state.activeInputIndex + 1].focus();
};
handleFocusPrevious = () => {
if (this.state.activeInputIndex - 1 < 0) {
return;
}
refs[this.state.activeInputIndex - 1].focus();
};
handleLogin = () => {
console.log("ACTIOn");
// this.formik.handleSubmit();
};
render() {
return (
<View style={styles.safeAreaView}>
<SafeAreaView style={styles.safeAreaView}>
<ScrollView style={styles.superView}>
<Formik {/* LINE 56 */}
initialValues={initialValues}
onSubmit={values => Alert.alert(JSON.stringify(values))}
validationSchema={schemaObject}
ref={p => (this.formik = p)}
>
{({
values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit
}) => (
<View style={styles.superView}>
<View style={styles.logoParentView}>
<Image
source={require("../../Resources/Assets/Login/aptihealth_logo.png")}
resizeMode={"contain"}
style={styles.logo}
/>
</View>
<View style={styles.emailParentView}>
<Text style={styles.titleLabel}>Email Id</Text>
<TextInput
value={values.emailId}
onChangeText={handleChange("emailId")}
onBlur={() => setFieldTouched("emailId")}
placeholder="Email Id"
style={styles.textInput}
autoCorrect={false}
onFocus={this.handleFocus(0)}
ref={input => {
refs[0] = input;
}}
/>
{touched.emailId && errors.emailId && (
<Text style={{ fontSize: 10, color: "red" }}>
{errors.emailId}
</Text>
)}
</View>
<View style={styles.passwordParentView}>
<Text style={styles.titleLabel}>Password</Text>
<TextInput
value={values.password}
onChangeText={handleChange("password")}
placeholder="Password"
onBlur={() => setFieldTouched("password")}
style={styles.textInput}
autoCorrect={false}
secureTextEntry={true}
onFocus={this.handleFocus(1)}
ref={input => {
refs[1] = input;
}}
/>
{touched.password && errors.password && (
<Text style={{ fontSize: 10, color: "red" }}>
{errors.password}
</Text>
)}
</View>
<View style={styles.forgotPasswordParentView}>
<TouchableOpacity
style={styles.forgotpasswordButton}
activeOpacity={0.7}
>
<Text>Forgot Password?</Text>
</TouchableOpacity>
</View>
<View style={styles.loginParentView}>
<TouchableOpacity
onPress={() => {
console.log("VALUES: ", values, this.formik);
this.handleLogin();
}}
style={styles.loginButton}
activeOpacity={0.7}
>
<Text style={styles.loginText}>Login</Text>
</TouchableOpacity>
</View>
<View style={styles.seperaterParentView}>
<View style={styles.seperaterView} />
<Text style={styles.seperaterText}>OR</Text>
<View style={styles.seperaterView} />
</View>
<View style={styles.faceIdLoginParentView}>
<Image
source={require("../../Resources/Assets/face_id_small_color/face_id_small_color.png")}
resizeMode={"contain"}
/>
<TouchableOpacity style={styles.faceIdButton}>
<Text>Sign In with Face ID</Text>
</TouchableOpacity>
</View>
<View style={styles.signUpParentView}>
<TouchableOpacity style={styles.signupButton}>
<Text>Sign Up for Account Here</Text>
</TouchableOpacity>
</View>
</View>
)}
</Formik>
</ScrollView>
</SafeAreaView>
<KeyboardAccessoryNavigation
nextDisabled={false}
previousDisabled={false}
nextHidden={false}
previousHidden={false}
onNext={this.handleFocusNext}
onPrevious={this.handleFocusPrevious}
avoidKeyboard
/>
</View>
);
}
}
I am trying to console formik ref in login action getting undefined value with debug error
ExceptionsManager.js:126 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of `LoginView`.
in Formik (at LoginView.js:56)
I have no idea why it's getting undefined ??

You should take a look at this issue.
You problem is here
<Formik
initialValues={initialValues}
onSubmit={values => Alert.alert(JSON.stringify(values))}
validationSchema={schemaObject}
ref={p => (this.formik = p)} {/* passing this ref will throw the error */}
>
In the latest version of Formik, they changed Formik to a functional component as explained in the issue, which gives you this error if you pass ref's.
You can check for the suggestions on the issue or wait until they release an update with the correction.
Edit:
Formik made an update and now you can use ref with the prop innerRef.
Please see this comment
You should change it to
<Formik
initialValues={initialValues}
onSubmit={values => Alert.alert(JSON.stringify(values))}
validationSchema={schemaObject}
{/* using innerRef instead of ref*/}
innerRef={p => (this.formik = p)} {/* this will give you the formik bag */}
>
And this way you can call this.formik.handleSubmit(), just lik you want to do.

Related

The text is not output to the form

I'm new to react native, trying to write a small project (notebook). I implemented the buttons, they all work, but when I save an entry to the form, nothing is displayed and does not give errors.
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Modal, Text, View, ScrollView, TextInput, Image, Button } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import React, {useState} from 'react';
import { Formik } from 'formik';
export default function App() {
const [news, setnews] = useState([
{name: '', text: ''}
]);
const addArtical = (artical) => {
setnews((list) => {
artical.key = Math.random().toString();
return[
artical,
...list
]
});
setModalWindow(false);
}
const [modalWindow, setModalWindow] = useState(false);
return (
<View style={styles.container}>
<Text style={styles.title}>Main tasks:</Text>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<View style={styles.up}>
<View style={styles.cub}></View>
<View style={styles.cub}></View>
<View style={styles.cub}></View>
<View style={styles.cub}></View>
<View style={styles.cub}></View>
<View style={styles.cub}></View>
</View>
</ScrollView>
<ScrollView vertical={true} showsVerticalScrollIndicator={false}>
<View style={styles.all}>
<TextInput style={styles.d} placeholder=' Search'></TextInput>
<Text style={styles.titletwo}>Other tasks:</Text>
<Ionicons style={styles.ic} name="add" size={30} color="black" onPress={() => setModalWindow(true)}/>
</View>
<Modal visible={modalWindow}>
<Ionicons style={styles.ic} name="close" size={30} color="black" onPress={() => setModalWindow(false)}/>
<View>
<Formik initialValues={{name: '', text: ''}} onSubmit={(values, clean) => {
addArtical(values);
clean.resetForm();
}}>
{(props) => (
<View >
<TextInput value={props.values.name} placeholder='Name' onChangeText={props.handleChange('name')}/>
<TextInput value={props.values.text} multiline placeholder='Text' onChangeText={props.handleChange('text')}/>
<Button title='Add' onPress={props.handleSubmit}/>
</View>
)}
</Formik>
</View>
</Modal>
</ScrollView>
</View>
);
}
Read more: I click add entry, a field opens with the addition of text, fill in the field with the necessary information, save, everything is successful, but my entry is nowhere

How can I send text taken from user to another screen to display it in <text> in react native? (I am a beginner)

App.js
function App() {
const [getScreen, showScreen] = useState(false);
const [getEmail, setEmail] = useState("");
return (
<View>
<LoginScreen />
<LoginContexts.Provider
value={{ getEmail, setEmail, getScreen, showScreen }}
>
{showScreen ? <Screen2 /> : <LoginScreen />}
</LoginContexts.Provider>
</View>
);
}
export default App;
Login
function LoginScreen({ navigation }) {
const { getEmail, setEmail, showScreen } = useContext(LoginContexts);
function gotEmail(inputEmail) {
setEmail(inputEmail);
}
return (
<View style={styles.container}>
<Text style={styles.loginText}>Login</Text>
<View style={styles.textInput}>
<TextInput
placeholder="Please enter your email"
defaultValue={getEmail}
onChange={gotEmail}
/>
</View>
<View style={styles.space} />
<View style={styles.passwordStyle}>
<TextInput
placeholder="password"
secureTextEntry
autoCorrect={false}
autoCapitalize="none"
/>
</View>
<View style={styles.space} />
<View>
<Button
style={styles.loginBTN}
title="Login"
onPress={() => {
showScreen(true);
}}
/>
</View>
</View>
);
}
export default LoginScreen;
Screen 2
function Screen2() {
const { getEmail } = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>{getEmail}</Text>;
</View>
);
}
export default Screen2;
LoginContext <-- used context api
import { createContext } from "react";
export const LoginContexts = createContext({});
Could you please tell me that why data is not going to another page?
I used useContext but I am not able to send data to another page. Please help me to solve this problem.
In this I had taken input from user as email and I want to display that email in text view in another page but I am unable to do that. On button click nothing happens.

Change render in my screen with boolean React Native

I want to change my render in the same page when i click on next button. In my component i have my state next, setNext initialize to false. The onPress to my button i change my state to true, but nothing happen when i click to the next button !
Here you can see the Step1 to Step2 and understand more what i need ^^
const [next, setNext] = useState(false);
{!next ? (
//Step1
<BigButton
title="Suivant"
onPress={() => {
setNext(true);
}}
/>) : ( // Step2 )
}
And all my code here :
import React, { useState } from "react";
import { useNavigation } from "#react-navigation/core";
import BigButton from "../components/BigButton";
import BigTextInfos from "../components/BigTextInfos";
export default function SignUpScreen({ setToken }) {
const [next, setNext] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const toggleSwitchPassword = () => {
setShowPassword((prev) => !prev);
};
return (
<>
{!next ? ( // STEP 1
<View style={{ marginTop: 60 }}>
<BigTextInfos title="S'inscrire" subtitle="Vous êtes :" />
<View>
<View>
<TouchableHighlight
style={[styles.input, styles.inputGender]}
onPress={() => setGender("Mme")}
>
<Text style={[styles.darkGrey]}>Mme</Text>
</TouchableHighlight>
<TouchableHighlight
style={[styles.input, styles.inputGender]}
onPress={() => setGender("M.")}
>
<Text style={[styles.darkGrey]}>M.</Text>
</TouchableHighlight>
</View>
<TextInput
style={[styles.input]}
placeholder="Prénom"
placeholderTextColor={colors.darkgrey}
/>
<TextInput
style={[styles.input]}
placeholder="Nom"
placeholderTextColor={colors.darkgrey}
/>
<BigButton
title="Suivant"
onPress={() => {
setNext(true);
}}
/>
</View>
</View>
) : ( // STEP 2
<View style={{ marginTop: 60 }}>
<BigTextInfos title="S'inscrire" subtitle="Finalisez l'inscription" />
<View>
<TextInput
style={[styles.input]}
placeholder="Email"
placeholderTextColor={colors.darkgrey}
/>
<View style={[styles.input]}>
<TextInput
style={{ width: 300 }}
placeholder="Mot de passe"
placeholderTextColor={colors.darkgrey}
color={colors.dark}
secureTextEntry={showPassword ? true : false}
/>
{showPassword ? (
<Feather
name="eye-off"
size={24}
color={colors.grey}
onPress={() => {
toggleSwitchPassword();
}}
/>
) : (
<Feather
name="eye"
size={24}
color={colors.grey}
onPress={() => {
toggleSwitchPassword();
}}
/>
)}
</View>
<BigButton
title="S'inscrire"
empty="false"
onPress={async () => {
console.log("s'inscrire");
const userToken = "secret-token";
setToken(userToken);
}}
/>
</View>
</View>
)}
</>
);
}
Its a bit odd that its not working, try different approach - separate the return logic inside a function and call the function on render. It should look a bit like this
const [next,setNext] = useState(false)
const handleSteps = () => {
if(next){
return <BigButton
title="Suivant"
onPress={() => {
setNext(true);
}}/>
}
return <SmallButton /> //here goes the other component
}
and then call that handleSteps function inside your return
return(
{handleSteps()}
)

React native loading data from async storage returns undefined

I am trying to load data from Async Storage in react-native. I managed to load all User Data from API to async storage and I was able to display it in UserData(), however when I try to return received data to other components I get:
[Unhandled promise rejection: SyntaxError: JSON Parse error: Unexpected identifier "undefined"]
I assumed data is returned asyncroniously so I tried to await it however then I was getting:
[Unhandled promise rejection: Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:]
I searched this error and all I found out was that it assumes I was calling hooks in class. It is not truth however, because I simply used setState() in UserProfile() to save data received from UserData(). Could anyone please tell me what am I doing wrong? Thanks for all the help in advance.
Function I used to retrieve user data from:
import React, { useState } from "react";
import { View, Text } from "react-native";
import storage from "./Storage";
export default function UserData() {
const [userData, setuserData] = useState();
storage
.load({
key: "user",
autoSync: true,
syncInBackground: true,
})
.then(ret => {
setuserData(JSON.stringify(ret.info));
})
.catch(err => {
// console.warn(err.message);
return;
});
if (userData) return userData;
}
Function which displays user profile:
import React, { useState, useEffect } from "react";
import DeleteUser from "./DeleteProfile";
import PayPal from "../navigation/PayPal";
import { Button, TextInput, Text, ScrollView, View, Image } from "react-native";
import Styles from "../../styles/Styles";
import storage from "../elements/Storage";
import UserData from "../elements/GetUserData";
export default function UserProfile() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [premium, setpremium] = useState(false);
const [admin, setAdmin] = useState(false);
const [id, setId] = useState("");
const [checkout, setCheckout] = useState(false);
const info = UserData();
useEffect(() => {
async function getData() {
const result = JSON.parse(info);
setEmail(result.email);
setName(result.name);
setId(result._id);
setPhone(result.phone);
setpremium(result.premium);
setAdmin(result.admin);
}
getData();
}, []);
return (
<ScrollView style={Styles.userProfile}>
<View style={(Styles.bigBar, { textAlign: "center" })}>
<Text style={Styles.h1}>Profile Page</Text>
</View>
<View style={Styles.profileContent}>
<View>
<Image
style={Styles.profileImg}
source={{
uri: "https://i.pinimg.com/736x/8b/16/7a/8b167af653c2399dd93b952a48740620.jpg",
}}
/>
</View>
<View>
<View>
<View style={Styles.profileView}>
<Text style={Styles.h6}>User name: </Text>
</View>
</View>
<View>
<View style={Styles.profileView}>
<Text style={Styles.h6}>Email: </Text>
</View>
</View>
<View>
<View style={Styles.profileView}>
<Text style={Styles.h6}>Phone number: </Text>
</View>
</View>
<View>
{/* {premium && ( */}
<View style={Styles.profileView}>
<Text style={Styles.h6}>premium: </Text>
</View>
{/* )} */}
</View>
<View>
{/* {!premium && ( */}
<View style={Styles.profileView}>
<Text style={Styles.h6}> premium: </Text>
<View
style={{
color: "red",
}}
></View>
</View>
{/* )} */}
</View>
{/* {admin && ( */}
<View style={Styles.profileView}>
<Text style={Styles.h6}>Admin: </Text>
<View style={{ color: "green" }}></View>
</View>
{/* )} */}
</View>
<View>
<View>
<View style={Styles.profileView2}>
<Text style={Styles.h6}>{name}</Text>
</View>
</View>
<View>
<View style={Styles.profileView2}>
<Text style={Styles.h6}>{email}</Text>
</View>
</View>
<View>
<View style={Styles.profileView2}>
<Text style={Styles.h6}>{phone}</Text>
</View>
</View>
<View>
{/* {premium && ( */}
<View style={Styles.profileView2}>
<View style={{ color: "orange" }}>
<Text style={Styles.h6}> POSIADANE</Text>
</View>
<View style={{ marginBottom: 1 }} />
</View>
{/* )} */}
</View>
<View>
{/* {!premium && ( */}
<View style={Styles.profileView2}>
<View
className="inline"
style={{
color: "red",
}}
>
{checkout ? (
<View style={{ margin: 0 }}>
<PayPal />
</View>
) : (
<View style={Styles.smlBtn}>
<Button
style={{ marginLeft: 1, marginBottom: 1 }}
onPress={() => setCheckout(true)}
title="BUY PREMIUM"
/>
</View>
)}
</View>
</View>
{/* )} */}
</View>
{/* {admin && ( */}
<View style={Styles.profileView2}>
<View style={{ color: "green" }}>
<Text style={Styles.h6}> TRUE</Text>
</View>
</View>
{/* )} */}
</View>
</View>
<View>
{/* <DeleteUser id={id} /> */}
{/* <Link
className="rf update better"
to={{
pathname: `/editProfile/?${id}`,
}}
>
UPDATE
</Link> */}
</View>
</ScrollView>
);
}
Okay I figured it out. In order to execute it properly I had to handle Promise returned from UserData() with then. It looks like this in the end:
useEffect(() => {
async function getData() {
await UserData().then(result => {
setEmail(result.email);
setName(result.name);
setId(result._id);
setPhone(result.phone);
setpremium(result.premium);
setAdmin(result.admin);
});
}
getData();
}, []);

React Native: FlatList Opens Modal for all Items Instead of Selected Item

I am using React Native FlatList and React Native Modal.
Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).
However, if there are 4 items in the FlatList, selecting 1 item causes
all 4 modals to pop up.
Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?
Code Snippet below (some lines of code were removed as it's not needed):
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false,
}
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
viewModal(item, price) {
const { modalVisible } = this.state;
return (
<Modal
statusBarTranslucent={true}
animationType={"slide"}
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View>
<View>
<View>
<Text>
Appointment Start Time:
</Text>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
Appointment End Time:
</Text>
<Text>
{moment(item.end_start).format('h:mm a')}
</Text>
</View>
<View style={styles.row}>
<Text>
Total:
</Text>
<Text>
{price}
</Text>
</View>
<View>
<View>
<Button
mode="outlined"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Cancel'}
</Button>
</View>
<View>
<Button
mode="contained"
onPress={() => {
this.setModalVisible(!modalVisible);
}}
>
{'Accept'}
</Button>
</View>
</View>
</View>
</View>
</Modal>
);
}
viewFreelancerTime() {
return (
<View>
<FlatList
renderItem={({ item }) => {
let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
return (
<Container>
{this.viewModal(item, totalPrice)}
<TouchableNativeFeedback
onPress={() => {
this.setModalVisible(true);
}}
>
<View>
<View>
<Text>
{moment(item.time_start).format('h:mm a')}
</Text>
</View>
<View>
<Text>
{totalPrice}
</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
}}
/>
</View>
);
}
render() {
return (
<>
<View style={{ flex: 1 }}>
{this.viewFreelancerTime()}
</View>
</>
);
};
The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.
To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.
UPDATE
Just something like this:
import React, {useState} from "react";
import { Modal } from "react-native";
export default function MyFlatList(props) {
const [selectedItem, setSelectedItem] = useState(null);
const handleOnSelectItem = (item) => {
setSelectedItem(item);
};
const handleOnCloseModal = () => {
setSelectedItem(null);
};
renderItem = ({ item }) => {
return (
<Container>
<TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
<View>
<View>
<Text>{moment(item.time_start).format("h:mm a")}</Text>
</View>
<View>
<Text>{totalPrice}</Text>
</View>
</View>
</TouchableNativeFeedback>
</Container>
);
};
return (
<View>
<FlatList renderItem={this.renderItem} />
<CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
</View>
);
}
export function CustomModal(props) {
const { isVisible, item, onClose, /*...*/ } = props;
// Play with the item data
let totalPrice = (
parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
).toFixed(2);
return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}
I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.
Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible

Categories