I'm new to react-native and when i was creating my app i couldn't get auto add button disabling to work
I set up these properties as a hooks:
const [taskText, setTaskText] = useState(null);
const [isDisabled, setDisabled] = useState('true');
then i have my function:
const addActivity = () => {
if (taskText == null){
setDisabled('true');
}else{
setDisabled('false');
}
}
but when i use isDisabled on touchableOpacity's disable prop its not updating
textInput prop :
onChange={() => addActivity()}
TouchableOpacity:
<TouchableOpacity style={styles.addWrapper} onPress={() => handleNewTask()} disabled={isDisabled}>
<Text style={styles.addText}>+</Text>
</TouchableOpacity>
setTaskText: (called by TextInput)
onChangeText={text => setTaskText(text)}
Doesn't TextInput update itself when i delete all the text?
What do i do to fix this?
It is because you use : useState('true') and you always use a string in your state.
A string, when it is not empty, is always true.
You must use the boolean values not string. You can therefore change to : useState(true) and change addActivity to :
const addActivity = () => {
if (taskText == null){
setDisabled(true);
}else{
setDisabled(false);
}
}
Related
const { useState, useEffect } = React;
const Thingy = ({ ...props }) => {
const [tenure, setTenure] = useState(null);
// state to hold tenure-dates (array of varying size)
const [tnDates, setTnDates] = useState(null);
const handleTenureChange = ev => setTenure(ev.target.value);
useEffect(() => setTnDates(
(tenure && tenure > 0)
? ([...Array(+tenure).keys()].map(
id => ({ id, tenureDate: '' })
)) : null
), [tenure]);
const handleDateChange = ev => {
const idx = ev.target.id;
const val = ev.target.value;
setTnDates(prev => {
const nd = [...prev];
nd[idx].tenureDate = val;
return nd;
});
};
above is the snippet for rendering tenure number of tenuredata where tenure is input
from user.
i want to clear all the tenure data input fields on a single button click. please help on this.
Since the input fields are presumably the states of both
const [tenure, setTenure] = useState(null);
const [tnDates, setTnDates] = useState(null);
To 'clear' the above state you need to reset it to it's original falsy value.
Since both the states you want to set are initially set to null you simply need to set their state to null again.
// button that clears the form fields
<button onClick={() => {
setTenure(null);
setTnDates(null);
}
Make sure you are using controlled components.
const handleClearInputs = () => {
setTenure(null)
setTnDates(null);
}
return(
<input value={tenure} />
<input value={tnDate} />
<button onClick={handleClearInputs}>Clear</button>
)
I'm trying to have a header button in my screen that access the screen state and validates if a post is ready to send to the DB (ie. its not an empty string and if theres an image post the image too).
When i create a full post (image and text) and try and fire the post function in the screen, it works (see the 'test' button on the screen)
When i create a full post (image and text) and try and fire the post function from the header, it does not grab the state properly.
What is happening here? How do i resolve this?
Screen State & Header
function PostScreen(props) {
const [newPost, setNewPost] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [mediaAsset, setMediaAsset] = useState('')
React.useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => {
return (
isLoading ? <ActivityIndicator color='#4292CE' size='small' style={{ marginRight: 10 }} /> :
<TouchableOpacity style={styles.postButton} onPress={() => postFunction()}>
<Text style={styles.postText}>Post</Text>
</TouchableOpacity>)
},
title: null
});
}, [isLoading, newPost]);
Post Function
let postFunction = async () => {
setIsLoading(true)
if (newPost === '') {
console.log(newPost) // this comes up as nothing when the postFunction is executed from the header
}
else if (mediaAsset === '') {
//! post only the text
}
else if (mediaAsset != ''){
//! post the text and the image
}
setIsLoading(false)
}
Test Environment
I'm currently trying to assign different objects to a let variable using if statements before displaying it. For some reason, assigning the object in a function will result in the variable displaying nothing instead of displaying the assigned object.
Let me give an example:
In the following code I am assigning 2 different icons to a let variable depending on a prop I passed from another file and the result matches exactly what I'm looking for.
const BottomBar = ({ screen }) => {
let icon;
if (screen === 'Home') {
icon = (<Entypo name='home' size={30} color='white'/>);
} else {
icon = (<SimpleLineIcons name='home' size={24} color='white'/>);
}
return (
<View>{icon}</View>
);
}
However, if I was to re-attempt this same example with a combination of a function with the useEffect hook, the {icon} object displays nothing.
const BottomBar = ({ screen }) => {
let icon;
const checkScreen = () => {
if (screen === 'Home') {
icon = (<Entypo name='home' size={30} color='white'/>);
} else {
icon = (<SimpleLineIcons name='home' size={24} color='white'/>);
}
};
useEffect(() => {
checkScreen();
});
return (
<View>{icon}</View>
);
}
Right away, I can assume this is because the function is asynchronous. The let variable "icon" is being used before assigning it with an object. So a simple fix would be something such as the following:
const BottomBar = ({ screen }) => {
const checkScreen = () => {
if (screen === 'Home') {
return (<Entypo name='home' size={30} color='white'/>);
} else {
return (<SimpleLineIcons name='home' size={24} color='white'/>);
}
};
let icon = checkScreen();
return (
<View>{icon}</View>
);
}
But what if I want to re-assign the variable icon with a different object later on after a button is pressed. Simply assigning an object right away doesn't allow me to re-assign it later. What I would like to do is assign the object as needed. What am I missing? Am I passing the variable wrong or is this simply not possible?
The let variable doesn't make the page rerender after the assignment so the value is changed but the react component doesn't read it, use the useState hook instead for purposes like this, but I advise you to use conditional rendering for your case like this:
const BottomBar = ({ screen }) => {
const checkScreen = () => {
if (screen === 'Home') {
return (<Entypo name='home' size={30} color='white' />);
} else {
return (<SimpleLineIcons name='home' size={24} color='white' />);
}
};
return (
<View>{checkScreen()}</View>
);
}
so i have this code:
<Icon
onPress={async () => {
if (favorites.some(item => item == post.id)) {
favorites.splice(favorites.indexOf(post.id), 1)
const newFavoritesArrayWithoutClickedID = favorites
await AsyncStorage.setItem("favorites", JSON.stringify(newFavoritesArrayWithoutClickedID))
} else {
const newFavoritesArray = [...favorites, post.id]
await AsyncStorage.setItem("favorites", JSON.stringify(newFavoritesArray))
}
}}
name={favorites.some(item => item == post.id) ? "heart" : "heart-outline"}
type='ionicon'
color={Colors.primaryRed}
/>
But if i press the heart icon it will add to async storage and show it in favorites but the heart icon doesnt change untill next render. Is there any solution to change this icon dynamically?
UPDATE:
Here i declare FAVORITES state, fetch it from Async Storage and fet it to favorites
const [favorites, setFavorites] = useState([])
useEffect(() => {
async function fetchData() {
let response = await AsyncStorage.getItem("favorites");
response = JSON.parse(response)
response !== null && !favorites.includes(...response) && setFavorites([...favorites, ...response])
response = ""
}
fetchData()
}, [])
You forgot to set state favorites state in onPress function. I would suggest ot modify your code in this way:
<Icon
onPress={async () => {
if (favorites.some(item => item == post.id)) {
const newFavoritesArrayWithoutClickedID = [...favorites];
newFavoritesArrayWithoutClickedID.splice(newFavoritesArrayWithoutClickedID.indexOf(post.id), 1);
setFavorites(newFavoritesArrayWithoutClickedID); //<-- set new state
await AsyncStorage.setItem("favorites", JSON.stringify(newFavoritesArrayWithoutClickedID));
} else {
let newFavoritesArray = [...favorites];
newFavoritesArray = [...newFavoritesArray , post.id];
setFavorites(newFavoritesArray); //<-- set new state
await AsyncStorage.setItem("favorites", JSON.stringify(newFavoritesArray));
}
}}
name={favorites.some(item => item == post.id) ? "heart" : "heart-outline"}
type='ionicon'
color={Colors.primaryRed}
/>
Explanation: when you work with React, if you want to dynamic re-render something you have to use state variable. Your initial useEffect sets favourites the first time but if you change it on some logic (like onPress function) you should update state value if you want to re-render the icon.
I've a flatlist with a list of options and a checkboxes in each row, and I need a state for the checkboxex, using hooks. I tought create a key-value relationship, an associative array somehow, so I can access to the proper state using the "item.option" as key:
export default function Questions({ navigation, route }) {
const [questionNumber, setQuestionNumber] = useState(0);
const data = route.params.data;
const numberOfQuestions = Object.keys(data.questions).length;
const [selectedOption, setSelectedOption] = useState([null]);
const [toggleCheckBox, setToggleCheckBox] = useState([false])
[...]
const renderItemMultipleChoice = ({ item }) => {
console.log(toggleCheckBox[item.option],item.option); //******here I get undefined
return (
<View style={[styles.option]}>
<CheckBox style={styles.checkBox}
disabled={false}
value={toggleCheckBox[item.option]}
onValueChange={(newValue) => multipleChoiceHandler(item.option, newValue)}
/>
<Text style={styles.optionText}>{item.option}</Text>
</View>
);
};
const multipleChoiceHandler = (item, newValue) => {
var newHash = toggleCheckBox
newHash[item] = newValue;
setToggleCheckBox({toggleCheckBox: newHash});
}
useEffect(() => {
if (data.questions[questionNumber].type_option != "open_choice") {
for (i = 0; i < Object.keys(data.questions[questionNumber].options).length; i++) {
var newHash = toggleCheckBox
newHash[data.questions[questionNumber].options[i].option] = false;
//*******every checkbox is false at the beginning...
setToggleCheckBox({toggleCheckBox: newHash});
console.log("toggle checkbox:",toggleCheckBox[data.questions[questionNumber].options[i].option],
data.questions[questionNumber].options[i].option); //****** here I get all false, the value I setted.
}
setSelectedOption([null]);
}
}, [questionNumber])
return(
<FlatList style={styles.flatlistOption}
data={data.questions[questionNumber].options}
renderItem={renderItemMultipleChoice}
keyExtractor={(item) => item.option}
/>
)
}
I'm supernoob about react, so to insert the intial state of toggleCheckBox for each element (using the parameter option to refer to the proper array element), I've used a for cycle... I know it's not proper and quite spaghetti code. Btw it should work, but when I try to access from the checklist to the toggleCheckBox state I get a undefined, so the checkbox state doesn't work properly. I don't know what I'm missing...