In my Todo App i have sucessfully implemented the add and delete functions but the update function is having trouble. What i need it to do is when i click the touchable opacity of a Todo, it should appear in my inputbox and if any change is made then that todo should be updated e.g clicking on abcd must make it appear in input box and changes made to it should be updated. Picture is also added below
export default function Todo() {
const [getText, setText] = useState('');
const [getList, setList] = useState([]);
const addItem = () => {
setList([...getList, {key: Math.random().toString(), data: getText}]);
setText('');
}
const removeItem = (itemKey) => {
setList(() => getList.filter(item => item.key != itemKey));
}
const updateFieldChanged = (index) => e => {
let newArr = [...getList]; // copying the old datas array
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setList(newArr);
}
return (
<View style={styles.container}>
<Text style={styles.title}>todo</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Enter Item"
onChangeText={text => setText(text)}
value={getText}
/>
<CustomButton
text = 'add'
color='red'
title= 'add'
textSize={20}
textColor="white"
onPressEvent={addItem}
/>
</View>
<ScrollView style={styles.scrollview}>
{getList.map((item, id) =>
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id)}
>
<View style={styles.scrollviewItem}>
<Text style={styles.scrollviewText}>{id}) {item.data}</Text>
<TouchableOpacity
onPress={() => removeItem(item.key)}
>
<View style={styles.crosstextcontainer}>
<Text style={styles.crosstext}>X</Text>
</View>
</TouchableOpacity>
</View>
</TouchableOpacity>
)}
</ScrollView>
</View>
);
}
Change
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id)}
>
to
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id,getText)}
>
Here iam passing the text that you need to enter to update a particular field
change your updateFieldChanged like this:
const updateFieldChanged = (index, text) => {
let newArr = [...getList]; // copying the old datas array
newArr[index].data = text; // replace e.target.value with whatever you want to change it to
setList(newArr);
setText('');
}
Here iam assigning the text you entered in the TextInput to the data object, which will update the array.
Hope this helps!
Related
I have a Flatlist that renders multiple posts, each post has a text section, the text can get very big so I'm using a show more method to expand/hide the text, and I'm handling the status of it using a state, however, when I click on a post to expand the text, it expands all posts in the Flatlist, I tried creating a dynamic Ref for each post, but I can't figure out a way to change the text content accordingly, anything I'm missing?
here's my code:
const [showMore, setShowMore] = useState(false);
const refs = useRef([]);
// Inside a Flatlist render item:
<View style={styles.postContainer}>
{item.data.postText.length > 120 ? (
showMore ? (
<TouchableOpacity onPress={() => setShowMore(!showMore)}
ref={(expandableText) => (refs.current[index] = expandableText)}>
<Text style={styles.postDescription}>{item.data.postText}</Text>
<Text style={styles.seeMore}>Show less</Text>
</TouchableOpacity>
) : (
<TouchableOpacity onPress={() => setShowMore(!showMore)}>
<Text style={styles.postDescription}>
{`${item.data.postText.slice(0, 120)}... `}
</Text>
<Text style={styles.seeMore}>Show more</Text>
</TouchableOpacity>
)
) : (
<Text style={styles.postDescription}>{item.data.postText}</Text>
)}
</View>
You are using the same state for all items of the FlatList. Hence, if you change the state, all items will be expanded. You could keep a boolean array as a state. The index of this state array corresponds to the index of a component inside the flatlist.
// data is the data of your FlatList
// we use this to initialize each show more value with false
const [showMore, setShowMore] = useState(data.map(data => false))
In your render function you use it as follows.
renderItem={({item, index}) => {
return <View style={styles.postContainer}>
{item.data.postText.length > 120 ? (
showMore[index] ? (
<TouchableOpacity onPress={() => handleShowMore(index)}
ref={(expandableText) => (refs.current[index] = expandableText)}>
<Text style={styles.postDescription}>{item.data.postText}</Text>
<Text style={styles.seeMore}>Show less</Text>
</TouchableOpacity>
) : (
<TouchableOpacity onPress={() => handleShowMore(index)}>
<Text style={styles.postDescription}>
{`${item.data.postText.slice(0, 120)}... `}
</Text>
<Text style={styles.seeMore}>Show more</Text>
</TouchableOpacity>
)
) : (
<Text style={styles.postDescription}>{item.data.postText}</Text>
)}
</View>
}
The handleShowMore function is as follows.
function handleShowMore(index) {
setShowMore(prev => prev.map((element, idx) => {
if(idx === index) {
return !element
}
return element
}))
}
I am working on the modal picker component that lets users sort the cryptocurrencies based on Market Cap and Volume key.
I have implemented the layout and onPress event, but I don't know how to apply color (#03AE9D) and toggle sort icons (sort-desc or sort-asc) on it properly when the item is selected.
Here is my code snippet. For the sake of simplicity, I removed the StyleSheet code.
The complete version could be found here.
// app/components/ModalPicker.js
const OPTIONS = ['market cap', 'volume'];
const ModalPicker = (props) => {
const [isSelected, setSelected] = useState(false);
const color = useMemo(() => {
return isSelected ? '#03AE9D' : '#676767cf';
}, [isSelected]);
const onPressCallback = useCallback(() => {
setSelected((prev) => !prev);
}, [setSelected]);
const onPressItem = (option) => {
props.changeModalVisibility(false);
props.setData(option);
};
const option = OPTIONS.map((item, index) => {
return (
<TouchableOpacity
style={
index === OPTIONS.length - 1 ? styles.noBorderOption : styles.option
}
key={index}
onPress={() => onPressItem(item)}
>
<View style={styles.sort}>
<Text style={[styles.text, { color }]}>{item}</Text>
{/* <FontAwesome name='sort-desc' size={24} color='#676767cf' /> */}
{/* <FontAwesome name='sort-asc' size={24} color='#676767cf' /> */}
</View>
</TouchableOpacity>
);
});
return (
<TouchableOpacity
onPress={() => props.changeModalVisibility(false)}
style={styles.container}
>
<View style={styles.modal}>
<ScrollView>{option}</ScrollView>
</View>
</TouchableOpacity>
);
};
export default ModalPicker;
That's the whole point of the question. Any suggestions are welcome.
For anyone else having this issue. You can add a active index state and set it in the onPress callback:
const [activeIndex, setActiveIndex] = useState();
const onPressItem = (option, index) => {
setActiveIndex(index);
setSelected(true);
//props.changeModalVisibility(false);
//props.setData(option);
};
Then use it in the items map:
const option = OPTIONS.map((item, index) => {
return (
<TouchableOpacity
style={
index === OPTIONS.length - 1 ? styles.noBorderOption : styles.option
}
key={index}
onPress={() => onPressItem(item, index)}
>
<View style={styles.sort}>
<Text style={activeIndex === index && isSelected ? styles.text : styles.textInactive}>{item}</Text>
{/* <FontAwesome name='sort-desc' size={24} color='#676767cf' /> */}
{/* <FontAwesome name='sort-asc' size={24} color='#676767cf' /> */}
</View>
</TouchableOpacity>
);
});
See a snack here for working example https://snack.expo.io/#yentln/jealous-candy
I'm making an app in React Native. There are three components I'm currently concerned with:
AllList.js: A screen comprised of a search bar and a FlatList of RowCard.js instances.
RowCard.js: a custom TouchableHighlight component that displays an item from an API, and displays DrinkPopup.js when tapped by using a state stored in AllList.js.
DrinkPopup.js: A custom Modal component that needs to take an ID from whichever RowCard is tapped and use it to make an API call to get its own data.
I can't figure out how to take the ID from the RowCard or FlatList and pass it to DrinkPopup - how should I do it?
Relevant code for AllList:
export default function AllList() {
const [isLoading, setLoading] = useState(true);
const [drinkData,setDrinkData] = useState([]);
const [searchValue, onChangeText] = useState(''); //needed for search
const [reloading, setReloading] = useState(false);
const [modalVisible, setModalVisible] = useState(false); //normal modal visibility handler
useEffect (() => {
fetch('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=' + searchValue)
.then((response) => response.json())
.then((json) => setDrinkData(json.drinks))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
},[drinkData]);
return (
<View style = {styles.screen}>
<View style = {styles.searchSection}>
<TextInput
placeholder="Search Drinks..."
style={styles.input}
onChangeText={text => onChangeText(text)}
value={searchValue}/>
</View>
<FlatList
data={drinkData}
keyExtractor={({ idDrink }, index) => idDrink}
removeClippedSubviews={true}
initialNumToRender={5}
renderItem={({ item }) => (
<RowCard id={item.idDrink} image={item.strDrinkThumb} title={item.strDrink} alcontent={item.strAlcoholic}
ingredient1={item.strIngredient1} ingredient2={item.strIngredient2} ingredient3={item.strIngredient3} setModalVisible={setModalVisible}
/>
)}
extraData={reloading}
/>
<DrinkPopup modalVisible={modalVisible} setModalVisible={setModalVisible}/>
</View>
);
};
Relevant code for RowCard:
const RowCard = (props) => {
return(
<TouchableHighlight
style={styles.rowCard}
activeOpacity={0.6}
underlayColor={"white"}
onPress={() => {props.setModalVisible(true) }}
>
<View style={styles.rowCard}>
<Image source={{uri: props.image, width: 150, height: 150}} />
<View style={styles.textBox}>
<Text style={styles.titleText}>{props.title}</Text>
<Text style={styles.ingredient}> Main ingredients: {props.ingredient1}, {props.ingredient2}, {props.ingredient3} </Text>
</View>
</View>
</TouchableHighlight>
)
};
Relevant code for DrinkPopup:
const DrinkPopup = (props) => {
return(
<Modal isVisible={props.modalVisible}
onBackdropPress={()=>{props.setModalVisible(false)}} //allows closing modal by tapping outside it or back button
onBackButtonPress={()=>{props.setModalVisible(false)}}
animationIn={"slideInUp"}>
<View style={styles.infocard}>
<View style={styles.titleBox}>
<Text style={styles.header}>I HAVE NO IDEA WHAT YOU PICKED</Text>
</View>
</View>
</Modal>
)
}
I am not sure how to add a delete function in a FlatList. I know I can make different components, but I want to know how to do it within this one file. I've trying to figure this out for hours, but do not know how to do.
export default function test() {
const [enteredGoal, setEnteredGoal] = useState("");
const [courseGoals, setCourseGoals] = useState([]);
const goalInput = enteredText => {
setEnteredGoal(enteredText);
};
const addGoal = () => {
setCourseGoals(currentGoals => [
...currentGoals,
{ key: Math.random().toString(), value: enteredGoal }
]);
};
const removeGoal = goalId => {
setCourseGoals(currentGoals => {
return currentGoals.filter((goal) => goal.id !== goalId);
})
}
return (
<View style={styles.container}>
<View>
<TextInput
color="lime"
style={styles.placeholderStyle}
placeholder="Type here"
placeholderTextColor="lime"
onChangeText={goalInput}
value={enteredGoal}
/>
</View>
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={styles.listItem} >
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
</View>
)}
/>
<View>
<TouchableOpacity>
<Text style={styles.button} onPress={addGoal}>
Add
</Text>
</TouchableOpacity>
</View>
</View>
);
}
You just need to modify your code a bit to handle the delete button. Since you already have delete functionality, call that function when you click the delete button. That's it.
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
<TouchableOpacity onPress={() => removeGoal(itemData.item.key)}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
)}
/>;
EDIT
change your removeGoal function as below
const removeGoal = goalId => {
setCourseGoals(courseGoals => {
return courseGoals.filter(goal => goal.key !== goalId);
});
};
Hope this helps you. Feel free for doubts.
Hi I am doing some kind of form in react native and i have this checkbox component that works fine, but i cant get his value on my form.js. I have the same problem with other components like datepicker. im unable to pass the values
any suggestion would be aprreciated
this is my checkbox component
import { CheckBox } from 'react-native-elements';
export default class CheckBoxs extends Component {
constructor() {
super();
this.state = {
checked1: true,
checked2: false,
};
}
render() {
return (
<View style={styles.checkbox} >
<CheckBox
containerStyle={styles.checkbox}
textStyle={styles.checkboxTxt}
uncheckedColor={'#b3b4b5'}
checkedColor={"#911830"}
key={1}
title="Mujer"
value={1}
value="1"
checkedIcon="stop"
checked={this.state.checked1}
onPress={() => this.setState({ checked1: !this.state.checked1, checked2: this.state.checked1 })}
/>
<CheckBox
containerStyle={styles.checkbox}
textStyle={styles.checkboxTxt}
uncheckedColor={'#b3b4b5'}
checkedColor={"#911830"}
key={0}
title="Hombre"
value={0}
value="0"
checkedIcon="stop"
checked={this.state.checked2}
onPress={() => this.setState({ checked2: !this.state.checked2, checked1: this.state.checked2 })}
/>
</View>
);
}
}
this is my form code
import CheckBoxs from './CheckBoxs';
const PersonalForm = ({onSubmit, errorMessage}) => {
import CheckBoxs from './CheckBoxs';
const PersonalForm = ({onSubmit, errorMessage}) => {
const [vName, setvName] = useState('');
const [vSecondName, setvSecondName] = useState('');
const [vLastName, setvLastName] = useState('');
const [vSecondLastName, setvSecondLastName] = useState('');
const [vCellphone, setvCellphone] = useState('');
const [vBirthDate, setvBirthDate] = useState('');
const [vRFC, setvRFC] = useState('');
const [vGender, setvGender] = useState('');
const [vEmail, setvEmail] = useState('');
const [checked] = useState('false');
return (
<ScrollView>
<View style={styles.buttonContainer}>
<View style={styles.inputContainer}>
<TextInput style={styles.inputs}
placeholder="Apellido materno"
onChangeText={newvSecondLastName => setvSecondLastName(newvSecondLastName)}
underlineColorAndroid='transparent'
value={vSecondLastName}
autoCorrect={false}
autoCapitalize='characters'
/>
</View>
<View>
<MyDatePicker />
</View>
<View style={styles.checkbox} >
<CheckBoxs />
</View>
<View style={styles.inputContainer}>
<TextInput style={styles.inputs}
placeholder="Correo electrónico"
underlineColorAndroid='transparent'
onChangeText={newvEmail => setvEmail(newvEmail)}
value={vEmail}
autoCorrect={false}
autoCapitalize='characters'
/>
</View>
</View>
<View style={styles.buttonContainer2}>
<TouchableOpacity
style={ styles.logout}
onPress={() => onSubmit(vName, vSecondName, vLastName, vSecondLastName, vCellphone, vBirthDate, vRFC, vGender, vEmail),console.log(vName, vSecondName, vLastName, vSecondLastName, vCellphone, vBirthDate, vRFC, vGender, vEmail)}
>
<Text style={styles.loginText}>GUARDAR</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
};
Have your component accept an 'onChange' callback (via props), and invoke it with the new value, whenever the value changes.
For example:
<Parent>
<Kid onChange={ newValue => { /* use the new value... */ } }/>
</Parent
Or another example:
const Parent = () => (
<View>
<TwoCheckboxes
onChange={
newValues => console.log('parent got new values', newValues)
}
/>
</View>
);
const TwoCheckboxes = props => {
const [ values, setValues ] = useState([ true, false]);
const [ val1, val2 ] = values;
const updateValuesAndReportChange = (newValues) => {
setValues(newValues);
props.onChange(newValues); /** this will pass updated values to Parent */
};
return (
<View>
<Checkbox
onPress={ () => updateValuesAndReportChange([ !val1, val2 ]) }
/>
<Checkbox
onPress={ () => updateValuesAndReportChange([ val1, !val2 ]) }
/>
</View>
);
};
In the React Native Docs:
This is a controlled component that requires an onValueChange callback that updates the value prop in order for the component to reflect user actions. If the value prop is not updated, the component will continue to render the supplied value prop instead of the expected result of any user actions.
So, add onValueChange to your Checkbox component. Store the value that it gives you in the component state, and pass that state to Checkbox through its value property.
An Example:
<Checkbox onValueChange={value => /** update **/} value={/** stored value **/} ... />