Reusing Component State issue - State always retained for last reference loaded - javascript

I tried creating a reusable component in React. Which has a textInput and secure text entry handled in the state of the reusable component. But the state is not getting maintained differently when reusing always the last state is updated,
Issue: If i call the reusable const two times on a single screen or on the next screen in stack. The toggle for secure entry keeps changing for the last field loaded and earlier loaded fields state is lost.
i.e., when i click on toggle of Password, text change from hidden to visible or vice-versa happens for Confirm password field.
This is how i call
<View style={styles.inputContainerView}>
<InputTextFieldView
enteredText={passwordEntered}
setEnteredText={setPasswordEntered}
title={Constants.registerPasswordPlaceholder} icon={lockIcon}
isSecureEntry={true}
placeholder={Constants.registerPasswordPlaceholder} />
</View>
<View style={styles.inputContainerView}>
<InputTextFieldView
enteredText={confirmPasswordEntered}
setEnteredText={setConfirmPasswordEntered}
title={Constants.registerConfirmPasswordPlaceholder} icon={lockIcon}
isSecureEntry={true}
placeholder={Constants.registerConfirmPasswordPlaceholder} />
</View>
My component:
const InputTextFieldView = ({ enteredText, setEnteredText, title, icon, isSecureEntry, placeholder }) => {
const [isSecureEntryEnabled, setIsSecureEntryEnabled] = useState(isSecureEntry)
const eyeOpenIcon = require('../../../assets/visibility.png')
const eyeCloseIcon = require('../../../assets/close-eye.png')
useEffect(() => {
console.log('called')
}, [])
toggleSecureTextEntry = () => {
console.log('title', title)
setIsSecureEntryEnabled(!isSecureEntryEnabled)
}
return (
<View style={styles.fieldsContainerView}>
<Text style={styles.titleStyle}>{title}</Text>
<View style={[styles.fieldInputContainerView, {padding: Platform.OS === 'ios' ? 12 : 0}]}>
<Image source={icon} style={styles.fieldIconView} />
<TextInput secureTextEntry={isSecureEntryEnabled} style={{ width: isSecureEntry ? '75%' : '85%' }} onChange={() => setEnteredText()} value={enteredText} placeholder={placeholder} />
{isSecureEntry &&
<TouchableWithoutFeedback onPress={() => toggleSecureTextEntry()}>
<Image source={isSecureEntryEnabled ? eyeOpenIcon : eyeCloseIcon} style={styles.fieldIconView} />
</TouchableWithoutFeedback>
}
</View>
</View>
)
}

I'm guessing that you are using isSecureEntry as the hook to toggle the password fields? If so, it looks like you are passing the same state to both
the password field and the confirm password field. Right now, you essentially have one light switch that controls two different lamps. So you are going to want to have separate separate useState hooks for the password field and confirm password field. Then pass each one to the correct component.
const [passwordSecure, togglePasswordSecure] = useState(true);
const [confirmPasswordSecure, toggleConfirmPasswordSecure] = useState(true);
const togglePasswordField = () => {
togglePasswordSecure(!passwordSecure)
};
const toggleConfirmPasswordField = () => {
toggleConfirmPasswordSecure(!confirmPasswordSecure)
};

Issue was happening due to TouchableWithoutFeedback. Now used TouchableOpacity and it started to work. Not sure why but it may help someone

Related

How to save TextInput with it's defaultValue if the user hasn't changed anything?

Okay so I hope this is easy, but I can't find anything about that on Google.
I have a details screen for a (cooking)receipe in my app. With the tap of a button the user can set a state isEditing, which then converts a heading into a text input. That heading displays {receipe.title} and I use the same value for the default value prop on the text input.
Once the user taps the edit button again, isEditing will be set to false and a update function will update the receipe in my Firebase database.
{!isEditing ? (
<Text style={styles.headingLarge}>{receipes.title}</Text>
):(
<TextInput
placeholder='Titel'
autoFocus={true}
defaultValue={receipes.title}
style={styles.headingLarge}
onChangeText={text => {
setPresentTitle(text);
}}/>
)}
It's all working, as long as the user actually changes something in the input. But the issue is that if a user doesn't change anything, onChangeText is never called and the database is updated with an empty string as it's title.
Is there a way to call the onChangeText when setting the defaultValue for this input (or another hack to set the setPresentTitle)?
Hey you can check this snack out, ive made it for you
This is the snack : https://snack.expo.dev/#gaurav1995/fascinated-donut
Hope it helps. feel free for doubts
import React,{ useState ,useEffect } from 'react';
import { StyleSheet, Text, TouchableOpacity, View ,TextInput ,Button } from 'react-native';
export default function App() {
const receipes = {
title:"Hey there"
}
const [isEditing, setEd] = useState(false)
const [text,setPresentTitle] = useState(receipes.title)
const toggleEdit = () => {
setEd(!isEditing)
}
useEffect(() => {
//update to firebase
//setFirebase(text)
},[text])
return (
<View>
{!isEditing ? (
<Text style={styles.headingLarge}>{text}</Text>
):(
<TextInput
placeholder='Titel'
autoFocus={true}
value={text}
style={styles.headingLarge}
onChangeText={text => {
setPresentTitle(text);
}}/>
)}
<Button title="Toggle Edit" onPress={toggleEdit} containerStyle={{marginTop:20}} />
</View>
)
}
const styles = StyleSheet.create({
container:{
flex:1,
padding:40
},
headingLarge:{
fontSize:40,
marginBottom:20
}
})

Top navigation items in header constantly flickers when typing

The image in the header constantly flickers when I type. May I ask how do I stop this flickering at the top right hand corner or accessoryRight? I am using this TopNavigation component from UI Kitten UI library. I don't think this is normal, it shouldn't happen at all. I must be doing something wrongly.
https://youtu.be/fQppQn-RzeE (How do I embed this? Editor, thank you in advance!)
The flickering happens in the title and the right side of the Navigation Header.
I made a separate component for the TopNavigation and then call it in respective screens.
Things I have tried:
Since the outcome of the Header relies on navigation props, I tried using useState and useEffect (with navProps as the dependency) to save the prop instead of reading directly from props, but to no avail
Directly adding the jsx into the TopNavigation's accessoryLeft/Right and title options
Any input is welcome, appreciate it!
TopNavigation:
const NavHeader = ({ navProps }) => {
const navigateBack = () => {
navProps.navigation.goBack();
};
const [type, setType] = React.useState('');
React.useEffect(() => {
setType(navProps.type);
}, [navProps]);
const BackIcon = props => <Icon {...props} name='arrow-back' />;
const BackAction = () => (
<TopNavigationAction icon={BackIcon} onPress={navigateBack} />
);
const renderBrand = () => (
<View style={styles.titleContainer}>
<Image source={require('../../assets/images/brand.png')} />
</View>
);
const renderLogo = () => (
<Image source={require('../../assets/images/logo.png')} />
);
return (
<TopNavigation
style={styles.topNav}
accessoryLeft={navProps.backNav && BackAction}
accessoryRight={
type === 'register' || type === 'profile' ? renderLogo : null
}
title={type === 'landing' || type === 'auth' ? renderBrand : null}
alignment='center'
/>
);
};
Import example:
<KeyboardAvoidingView
style={styles.kbContainer}
behavior={Platform.OS === 'ios' ? 'padding' : null}
>
<SafeAreaView style={styles.parentContainer}>
<NavHeader navProps={navProps} /> // Imported custom Header component here
<ScrollView>
{other content}
</ScrollView>
</SafeAreaView>
</KeyboardAvoidingView>
Perhaps requiring the images just one time and not on every render may help.
Try adding this at the top of the file (not inside a component function)
const brandImage = require('../../assets/images/brand.png');
const logoImage = require('../../assets/images/logo.png');
And then in your props instead of an inline require use the variables:
const renderBrand = () => (
<View style={styles.titleContainer}>
<Image source={brandImage} />
</View>
);
const renderLogo = () => (
<Image source={logoImage} />
);
Edit:
Since this didn't work, perhaps utilizing useMemo to memoize the components that show the images would work?
Something like
const renderBrand = useMemo(() => (
<View style={styles.titleContainer}>
<Image source={brandImage} />
</View>
),[]);
const renderLogo = useMemo(() => (
<Image source={logoImage} />
),[]);

How to pass Button component's title into a function in React Native

I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>

Function is not being called when state changes

This is my function for rendering items in a flatlist
renderItem = ({ item }) => {
var integer = Number(item.key)
return (
<View>
<Text style={styles.row}>
{item.text}
</Text>
<View style={{flexDirection:'row'}}>
{this.createButtonYes(integer)}
{this.createButtonNo(integer)}
{this.answer(this.state.buttonStates[integer])}
</View>
<Text >__</Text>
</View>
)
}
And the problem I am facing is the function this.answer is not being called when the state of buttonStates changes
answer = (val) => {
if(val){
return(
<Text style={{fontSize:20}}>YES</Text>
)
}else if(val==false){
return(
<Text style={{fontSize:20}}>NO</Text>
)
}else{
return(
<Text style={{fontSize:20}}>Not Answered</Text>
)
}
}
I assumed that every time the state changes the function would be called but that does not seem to be the case, so does anyone have a solution? What I want is whenever the buttons are pressed the state will change and then this.answer will take the changed state and display what it has to accordingly.
Thanks
EDIT:
Code for the button:
buttonYesHelp = num =>{
const newItems = [...this.state.buttonStates];
newItems[num] = true;
return newItems
}
createButtonYes = (num) => {
return(
<TouchableOpacity style={styles.buttonYes}
onPress =
{
()=> {{this.setState({ buttonStates:this.buttonYesHelp(num) })}}
}>
<Text style={styles.buttonTextStyle}>YES</Text>
</TouchableOpacity>
)
}
num is the index of the thing I want to change in the list
EDIT:
I have tried multiple different things but the problem I keep running into is that when I render the button I want it to react to a state variable but it never seems to change based on the state even when the state is changing.
For example, in this.answer I assumed that it would return the text based on the state of buttonStates but it seems to only account for the initial state and nothing after
I was able to achieve this in a different piece of code with identical syntax but for some reason this is not working

React Native TextInput save to const

Summary
I have a react native functional component where I collect a value from the user in TextInput and then need to pass that value to a function when a button is pressed. I know how to do this in a react native class component with state, however I'm trying to use a functional component.
I'm close to figuring it out but I'm just missing something. The code I have so far is below.
saveUserInput handles saves the text from the user and returns input. When I pass saveUserInput into forgotPassword, which is my function that sends userInput to my backend to reset password, saveUserInput is defined as the function as opposed to the value it returns. How can make this work as a functional component?
Code
export default (ForgotPasswordScreen = () => {
const saveUserInput = userInput => {
const input = userInput;
return input;
};
return (
<View style={styles.container}>
<Text style={styles.headerText}>Forgot Password</Text>
<Text style={styles.forgotText}>
Enter your username or email address below to reset your password
</Text>
<TextInput
onChangeText={userInput => saveUserInput(userInput)}
placeHolder={"username or email"}
/>
<Text
style={styles.buttonText}
onPress={saveUserInput => forgotPassword(saveUserInput)}
>
Forgot Password Button
</Text>
</View>
);
});
export default (ForgotPasswordScreen = () => {
let input = '';
const saveUserInput = userInput => {
input = userInput;
};
return (
<View style={styles.container}>
<Text style={styles.headerText}>Forgot Password</Text>
<Text style={styles.forgotText}>
Enter your username or email address below to reset your password
</Text>
<TextInput
onChangeText={userInput => saveUserInput(userInput)}
placeHolder={"username or email"}
/>
<Text
style={styles.buttonText}
onPress={() => forgotPassword(input)}
>
Forgot Password Button
</Text>
</View>
);
});
This should do the trick without using hooks. Pull out the input variable to be in scope for all other components to use.
But life could be much easier using useState hooks.

Categories