i'm trying to add items to a flatlist via textinput. at the moment i can only add one item and when i try to add a second it just updates the first item, although the data from the textinput should appear below the previous textinput. i have found a few instances of a similar kind of problem and i know that i probably need to add something to my code but i just can't figure out what. below is my code. i would be grateful for any kind of help :)
function FlatlistComponent({ }) {
const listItems = [];
const [arrayHolder, setArrayHolder] = React.useState([]);
const [textInputHolder, setTextInputHolder] = React.useState('');
useEffect(() => {
setArrayHolder(listItems)
}, [])
const joinData = () => {
listItems.push({ name : textInputHolder });
setArrayHolder(listItems);
}
const FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
backgroundColor: '#00678A',
alignSelf: 'center'
}} />
);
}
// Delete note
deleteNote = id => () => {
const filteredData = arrayHolder.filter(item => item.id !== id);
setArrayHolder({ data: filteredData });
}
return (
<View style={styles.MainContainer}>
<FlatList
data={arrayHolder}
width='100%'
extraData={arrayHolder}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={FlatListItemSeparator}
renderItem={({ item }) => <Text style={styles.item} onPress={deleteNote(item.id)}> {item.name} </Text>}
/>
<TextInput
placeholder='Kirjoita uusi'
onChangeText={data => setTextInputHolder(data)}
style={styles.textInputStyle}
underlineColorAndroid='transparent'
clearTextOnFocus={true}
value={listItems}
/>
<TouchableOpacity onPress={joinData} style={styles.button} >
<Text style={styles.buttonText}> + </Text>
</TouchableOpacity>
</View>
);
}
listItems is always an empty array after component re-rendered, you should concat previous arrayHolder with new item:
const joinData = () => {
setArrayHolder([... arrayHolder, {name: textInputHolder }]);
// or use update function
// setArrayHolder(prev => [...prev, {name: textInputHolder }]);
}
Related
I am attempting to press on this pressable button, and navigate to a new page. The tricky bit is that this Pressable item is part of a returned array, as there are multiple of them being rendered each with different data. I want each button to take me to a 'product page', each page being different depending on the button
Here is what i have so far:
The main function
const JobRequestList = () => {
const [data, setData] = useState([]);
useEffect(() => {
returnArray().then(data2 => {
setData(data2);
});
}, []);
if (data.length === 0) {
j = [];
return (
<ScrollView>
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
</ScrollView>
);
} else {
return <ScrollView>{data}</ScrollView>;
}
};
This requests the data, and returns it in a form that can be rendered. It either returns a no object, or an array of items from the below function - This is where my onPress is located, and have no idea how to implement a navigation fnction into it. Please note, i already have my navigation functions setup
const returnArray = async () => {
return queryData().then(() => {
return j.map(x => {
return (
<Pressable
key={x.id}
style={styles['wrapper' + x.data().PD]}
onPress={() => {}}>
<Text style={styles.text}> {x.data().PD} </Text>
<Text style={styles.text}> {x.data().address} </Text>
<Text style={styles.text}> {x.data().time} </Text>
</Pressable>
);
});
});
};
The above function then calls the below
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
j.push(doc);
});
});
};
Here is what my navigation functions should be inside this class - Again, which is already setup correctly
const navigation = useNavigation();
navigation.navigate('JobInfo');
Thankyou in advanced!
It is anti-pattern in React to store JSX in component state. React components's rendered UI is a function of state & props. Store the data in state and then render the data mapped to JSX.
Example:
queryData fetches firebase docs & data
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
const docs = [];
querySnapshot.forEach(doc => {
docs.push({
...doc,
data: doc.data(),
});
});
return docs;
});
};
Apply the navigation logic in the Pressable component's onPress handler when mapping the data state.
const JobRequestList = () => {
const navigation = useNavigation();
const [data, setData] = useState([]);
useEffect(() => {
queryData()
.then(data => {
setData(data);
});
}, []);
return (
<ScrollView>
{data.length
? data.map(el => (
<Pressable
key={el.id}
style={styles['wrapper' + el.data.PD]}
onPress={() => {
navigation.navigate('JobInfo');
}}
>
<Text style={styles.text}> {el.data.PD} </Text>
<Text style={styles.text}> {el.data.address} </Text>
<Text style={styles.text}> {el.data.time} </Text>
</Pressable>
))
: (
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
)
}
</ScrollView>
);
};
I am fetching an array of two objects. there are "title" and "iqtiboslar" array inside objects. and show it in SectionList but it is giving an error: "can not read properties of undefined(reading 'length')". Here is my code. Any ideas will be highly appreciated
const Item = ({iqtiboslar}) => (
<View>
<Text>{iqtiboslar}</Text>
</View>
);
const HomeScreen = ({navigation}) => { const [quote, setQuote] = useState();
useEffect(() => {
fetchQuotes();
return () => {
setQuote();
};
}, []);
const fetchQuotes = async () => {
try {
const quoteCollection = await firestore().collection('iqtiboslar').get(); // get(:field) to get specific doc
quoteCollection._docs.map(doc => setQuote(doc.data().items));
// quoteCollection._docs.map(doc => console.log(doc));
} catch (error) {
console.log(error);
}
};
return (
<View style={styles.container}>
{quote ? (
<SectionList
sections={quote}
keyExtractor={(item, index) => item + index}
renderItem={({item}) => <Item title={item.title} />}
renderSectionHeader={({section}) => <Text>{section.title}</Text>}
/>
) : (
<ActivityIndicator />
)} </View>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: 'red',
},
});
Try this code
<SectionList
sections={quote}
keyExtractor={(item, index) => item + index}
renderItem={({item}) => <Text>{item}</Text>}
renderSectionHeader={({section: {title}}) => (
<Text style={styles.text}>{title}</Text>
)}
i'm quite new to react-native, lately i'm struggling with multiselect and highlights my items in a flatlist, could anyone help me out please! :D
export default function opzioni( {navigation} ){
const renderItem = ({item}) => (
<TouchableOpacity style={item.stile} onPress={() => selectItem(item)}>
<Text>{item.titolo}</Text>
</TouchableOpacity>
)
const selectItem = (item) => {
item.isSelect = !item.isSelect;
item.stile = item.isSelect ? styles.itemSelected : styles.item;
}
return(
<SafeAreaView style={styles.containerUtente}>
<ScrollView>
<Text>SELEZIONA COSA ELIMINARE:</Text>
<FlatList
numColumns={4}
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
extraData={data}
/>
</ScrollView>
</SafeAreaView>
)
}
and the data is like so, i have an object with title to show in the flatlist, an id to give in the flatlist, isSelect for selecting or deselecting the item and lastly a stile for re-render the item in a different color background
{
titolo: "no alcol",
id: 1,
isSelect: false,
stile: styles.item
}
Create a state and store the data in that...then use it in FlatList..and your select function were also wrong
Working Example
Write it like this
export default function opzioni({ navigation }) {
const [Data, SetData] = React.useState(data);
const renderItem = ({ item }) => (
<TouchableOpacity
onPress={() => selectItem(item.id)}
style={item.isSelect ? styles.itemSelected : styles.item}>
<Text>{item.titolo}</Text>
</TouchableOpacity>
);
const selectItem = (id) => {
let temp = [...Data];
for (let i = 0; i < temp.length; i++) {
if (temp[i].id === id) {
temp[i].isSelect = temp[i].isSelect ? false : true;
break;
}
}
SetData(temp);
};
return (
<SafeAreaView style={styles.containerUtente}>
<ScrollView>
<Text>SELEZIONA COSA ELIMINARE:</Text>
<FlatList
numColumns={4}
data={Data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
</ScrollView>
</SafeAreaView>
);
}
I am getting data from the firebase but able to it on emulator I tried using consloe which workfine
const Getdata = async () => {
await firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
if(snapshot.key) {
console.log('key',snapshot.key);
let grabbedData = snapshot.val().orders;
grabbedData.map((order, i) => {
console.log('order',order.id);
console.log('order',order.avatar);
console.log('order',order.name);
console.log('order',order.price);
console.log('----------------');
});
}
});
}
Getdata();
After modifing the above code as below code nothing is showing to the screen
const Getdata = () => {
let data = firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
// something is wrong with this below statememnt I think
return (
<Card>
<Text>{snapshot.key}</Text>
{
snapshot.val().orders.map((order, i) => {
return (
<TouchableOpacity key={i} onPress={() => {
}}>
<Card>
<View style={styles.user}>
<Image
style={styles.image}
resizeMode="cover"
source={{ uri: order.avatar }}
/>
<View style={{flexDirection:'column', flex: 1}}>
<Text style={styles.name} h4>{order.name}</Text>
<Card.Divider style={{ marginTop: 25}}/>
<View style={{flexDirection:'row', flex: 1,justifyContent: 'space-between'}}>
<Text style={styles.price}>{order.price}</Text>
</View>
</View>
</View>
</Card>
</TouchableOpacity>
);
})
}
</Card>
)
})
return data;
}
and then <Getdata/>
Something I am doing wrong with first return statememnt but dont know what.
Edit I am adding a pic how data is organised
Try this way
const [orders, setOrders] = useState([]); // initially empty
const [key, setKey] = useState(undefined); // undefined empty
const Getdata = async () => {
await firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
if(snapshot.key) {
console.log('key',snapshot.key);
let grabbedData = snapshot.val().orders;
setKey(snapshot.key); // set key here
setOrders(grabbedData); // set orders here to state, it will rerender
}
});
}
useEffect(() => {
Getdata();
});
return (
<Card>
{key && <Text>{snapshot.key}</Text>}
{
orders.map((order, i) => {
return (
<TouchableOpacity key={i} onPress={() => {
}}>
.........
</TouchableOpacity>
);
})
}
</Card>
)
I'm trying to make a tag selection, the problem is, I don't know how to make a state for each item in the map, right now I have just one state, that, of course, will change all items.
That's the state and the function to toggle the state
const [selectedActivity, setSelectedActivity] = useState(false);
const toggleSelectedActivity = () => {
setSelectedActivity(!selectedActivity);
};
and that's the map function
<View style={styles.tags}>
{activitiesObject.map((data, i) => (
<TouchableOpacity
key={data.activity}
onPress={() => toggleSelectedActivity(i)}
>
<Text style={selectedActivity ? styles.selectedTag : styles.tagsText}>
{data.activity}
</Text>
</TouchableOpacity>
))}
</View>;
the image below shows what I expect to happen every time the user selects a tag
Here is the full code: https://snack.expo.io/KIiRsDPQv
You can do one of following options
change state to an array
const [selectedActivity, setSelectedActivity] = useState(Array.from({ length: activitiesObject.length }, _ => false))
const toggleSelectedActivity = (index) =>
setSelectedActivity(prev => prev.map((bool, i) => i == index ? !bool : bool))
while passing the index to function, and use selectedActivity[i] ? ...
extract
<TouchableOpacity key={data.activity} onPress={() => toggleSelectedActivity(i)}>
<Text style={selectedActivity ? styles.selectedTag : styles.tagsText}>{data.activity}</Text>
</TouchableOpacity>
to its own component, and inside it declare the state
{activitiesObject.map((data, i) => <MyComp data={data} i={i} />
const MyComp = ({ data, i }) => {
const [selectedActivity, setSelectedActivity] = useState(false)
return <TouchableOpacity key={data.activity} onPress={() => setSelectedActivity(prev => !prev)}>
<Text style={selectedActivity ? styles.selectedTag : styles.tagsText}>{data.activity}</Text>
</TouchableOpacity>
}