Fetch data in flat list from Firebase Real Time Database - firebase

I have a structure of data i want to fetch and display in a Flat List here is my data.
-Lt10FlMt0xtru36Ztmb
name : "Hello"
-Lt0pdC5Ikwd-ZWNBiYJ
name : "Coke"
-Lt0paPi_-zkCelfoisM
name : "Pespsi"
Here is my code:
readUserData = () => {
firebase.database().ref('Brands/').once('value').then((snapshot) => {
console.log(snapshot.val())
const userdata = snapshot.val();
// Alert.alert("Helo" , userdata);
this.setState({
getListbrands : userdata
})
Alert.alert("Data" , JSON.stringify(this.state.getListbrands))
});
}
and here i am setting data in Flat list but it is not showing any thing please guide.
{
this.state.getListbrands &&
<FlatList
data={this.state.getListbrands}
keyExtractor={(a, b) => b.toString()}
renderItem={({ item }) => (
<Text style={{color:'#000'}}>{item.name}</Text>
)}
/>
}

According to the documentation, FlatList component expects an array of data. The value of your snapshot is an object, and not an array.
If you want to use an array of the values of each child node, you can get that with:
firebase.database().ref('Brands/').once('value').then((snapshot) => {
userdata = [];
snapshot.forEach((child) {
userdata.push(snapshot.val());
})
...
You can then set this array to the FlatList.

{
this.state.getListbrands &&
<FlatList
data={this.state.getListbrands}
keyExtractor={(a, b) => b.toString()}
renderItem={(item) => (
<Text style={{color:'#000'}}>{item.name}</Text>
)}
/>
}
change like this.i think item in here is not a object.please try. it will work. thank you.

Related

Remove item from redux form field array by its name and not index

Redux Form has FieldArray field:
https://redux-form.com/6.0.0-rc.3/docs/api/fieldarray.md/
I am trying to delete multiple of items from it but remove() method only works for a single removal perhaps because each time the fields get one item smaller and the index determined by me is bigger than the fields array:
<MultiSelect
placeholder="Delete project group"
onChange={(v) => {
const diff = difference(addedGroups, v)
if (!isEmpty(diff)) {
const groupToDelete = diff[0]
forEach(projectsByGroup[groupToDelete], p => removeElement(addedProjects.indexOf(p)))
deleteGroup(groupToDelete)
}}
options={projectGroupNames}
value={addedGroups}
inline
/>
Where removeElement is fields.remove FieldArray function. How to remove correctly multiple items from FieldArray selectively?
Update:
I have also tried to use change in my reducers like that:
import { change } from 'redux-form'
export const deleteVariantSearchProjectGroup = (projectGroupGuid) => {
return (dispatch, getState) => {
const state = getState()
const projectsInGroup = state.projectsByProjectGroup[projectGroupGuid]
const allProjectFields = getProjectsFamiliesFieldInput(state)
const remainingProjectFields = allProjectFields.filter(projectField => !projectsInGroup.includes(projectField.projectGuid))
change(SEARCH_FORM_NAME, 'projectFamilies', remainingProjectFields)
dispatch({ type: UPDATE_VARIANT_SEARCH_ADDED_GROUPS, newValue: without(getState().variantSearchAddedProjectGroups, projectGroupGuid) })
}
}
I get correctly an array remainingProjectFields but then change(SEARCH_FORM_NAME, 'projectFamilies', remainingProjectFields) does not do anything.
I was not able to actually find a way to remove fields one by one with fields.remove but ultimately I solved it by using a reducer and updating Redux Form state using change method:
import { change } from 'redux-form'
export const deleteVariantSearchProjectGroup = (projectGroupGuid) => {
return (dispatch, getState) => {
const state = getState()
const projectsInGroup = state.projectsByProjectGroup[projectGroupGuid]
const allProjectFields = getProjectsFamiliesFieldInput(state)
const remainingProjectFields = allProjectFields.filter(projectField => !projectsInGroup.includes(projectField.projectGuid))
dispatch(change(SEARCH_FORM_NAME, 'projectFamilies', remainingProjectFields))
dispatch({ type: UPDATE_VARIANT_SEARCH_ADDED_GROUPS, newValue: without(getState().variantSearchAddedProjectGroups, projectGroupGuid) })
}
}
and deleteVariantSearchProjectGroup = deleteGroup in the very first jsx code snippet in the question.

mapping firebase read to props react-native

this might not be an issue with the mapping itself but i've read some data from my firebase realtime database into my sate and i'm trying to pass it as props then map it in a subsequent component.
I am getting the following error, I am using an android emulator:
TypeError: undefined is not a function (near ...'this.props.notes.map'...)
app.js (where I update the state)
state = {
loggedin: false,
notes: [
{
id: 1,
text: "mow the lawn",
author: "dean",
time: "10am"
},
{
id: 2,
text: "feed the dog",
author: "sam",
time: "2pm"
}
]
}
//passing props to notes component
<Notes style={styles.notes} notes={this.state.notes} />
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: snapshot.val() });
};
my Notes component where I map the props
renderCondition =()=>{
if(this.state.Deleted === false){
return(
<View>
{this.props.notes.map(note => (
<View
style={styles.note}
key={note.author}
id={note.id}
>
<Text style={styles.noteHeader}>{note.author}</Text>
<Text style={styles.noteText}>{note.text}</Text>
<Text style={styles.noteTime}>{note.time}</Text>
<Button title= 'X' onPress={() => this.setState({Deleted:true}) }></Button>
</View>
))}
</View>
)
}
return(
<View>
<Text>are you sure you want to delete this note?</Text>
<Button title="Yes"></Button>
<Button onPress ={()=>{this.setState({Deleted:false})}} title="No"></Button>
</View>
)
}
render() {
return (
<View>
{this.renderCondition()}
</View>
);
}
You have to check whether the notes have been passed down or whether they are undefined or null. JavaScript is not going to map an undefined object.
Try the following code:
{this.props.notes && this.props.notes.map(note => (
// do stuff with each note...
))}
This will run the .map function only if the notes are neither undefined nor null.
So my issue was that I needed to update my notes state by making sure it was an array. I had mistakenly simply updated it as an object and then attempted to map the object. Here is my solution.
from this
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: snapshot.val() });
};
to this
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: [snapshot.val()] });
};

Redux Remove one item from cart

I've build an app in react-native and i'm using react-redux to store data in my cart.
Currently when I add a product lets say apple to my cart. My cart will contain 1x apple and if I add the same product to my cart I will have 2x apple.
Problem
But if I try to remove 1x apple my cart removes all the apples in my cart.
Question
How can I remove the item in my cart by -1?
cartitems.js
case 'REMOVE_FROM_CART':
return state.filter(cartItem=>cartItem.id !==
action.payload.id )
CartProducts.js component
<View key={products.index} style={styles.appels}>
<View style={styles.iconContainer}>
<Icon name={products.item.icon} color="#DD016B" size={25} />
</View>
<View style={styles.text}>
<Text style={styles.name}>
{products.item.name}
</Text>
<Text style={styles.price}>
€ {products.item.price}
</Text>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => this.props.onPress(products.item)} >
<Icon style={styles.button} name="ios-remove" color="white" size={25} />
</TouchableOpacity>
</View>
</View>
CartScreen.js
<Products products={appels} onPress={this.props.addItemToCart}/>
CartScreen.js | handeling removing product from cart
const mapDispatchToProps = (dispatch) =>{
return{
removeItem:(product) => dispatch ({
type:'REMOVE_FROM_CART' , payload: product
})
}
}
I believe you are keeping duplicates of the same product in the state when multiple copies of the same product are added. You should therefore only filter the first match instead of all matches:
case 'REMOVE_FROM_CART': {
const index = state.findIndex(item => item.id === action.payload.id);
return state.filter((_, i) => i !== index);
}
This should get it to work while keeping your current design.
However, if the user removes a duplicated product from the bottom of the list, the UI might not be intuitive as the first copy of the product in the list will be removed from the UI instead, hence you might want to adjust your removeItem action payload to contain the index of the product to be removed instead of the product object.
A better approach might be to use a qty variable to indicate multiple copies, which will reduce the state size and make it easier to display a quantity column on the cart screen (instead of displaying duplicated products):
case 'ADD_ITEM_TO_CART':
if (state.some(item => item.id === action.payload.id)) {
// increase qty if item already exists in cart
return state.map(item => (item.id === action.payload.id ? { ...item, qty: item.qty + 1 } : item));
}
return [...state, { ...action.payload, qty: 1 }]; // else add the new item to cart
case 'REMOVE_FROM_CART':
return state
.map(item => (item.id === action.payload.id ? { ...item, qty: item.qty - 1 } : item))
.filter(item => item.qty > 0);
Your CartScreen.js will also need to be modified accordingly if you use this new design.
When doing
return state.filter(cartItem=>cartItem.id !== action.payload.id )
You are basically saying, return everything but this cartItem specificaly.
That is not what you want. You want to give :
A quantity to remove in the payload.
The id of the product.
So here is your action :
dispatch({type:'REMOVE_FROM_CART' , payload: {product, qty} })
Then you have to find your item and update it's quantity :
case 'REMOVE_FROM_CART':
const updatedProductList = state.productList.map( (product) => {
if(product.id === payload.product.id){ // this is the item we care about
return {
...product, // this will avoid a state mutation
qty : product.qty - payload.qty // update the qty
}
}else { // just return the element as it is
return cartItem
}
})
return updatedCartItemList;
break;
default :
return state
case UNFAVORITE: {
return {
favorite: [
...state.favorite.filter(favorite => favorite !== action.payload),
],
};
}
simply do this to UNFAVORITE
Well, you may need to pass either quantity or increment/decrement flag in your action payload.
your action creator may accept parameter for increment/decrement
const updateItem = dispatch=>(increment=true)=>{
dispatch({type:'UPDATE_IN_CART' , payload: product, increment})
}
Now in your reducer
case 'UPDATE_IN_CART':{
const {increment, payload}= action; //ES6 destructing
const updatedCart = state.map((cartItem)=>{
if(cartItem.id !== payload.id )){
cartItem.quantity = increment?cartItem.quantity+1:cartItem.quantity-1 //will handle increment/decrement;
}
return cartItem;
})
return updatedCart;
}

Rendering data in FlatList from firebase

I am using React Native 0.49. I have data fetched from firebase, list of users users/, each item in this list was set like this firebase.database().ref('users/' + userId).set(userInfo) userId is the uid of the currentUser.
Now I am fetching back (in actions - redux):
export function getPeople(){
return (dispatch) => {
dispatch(getPeopleRequest());
getData().then(result => {
dispatch(getPeopleSuccess(result.val()))
})
.catch(error => {
dispatch(getPeopleFailure(error))
});
}
}
const getData = () => {
const userRef = firebase.database().ref('users/').limitToFirst(20);
return userRef.once('value');
}
In component, I am trying to render the data in FlatList, but it's not rendering anything, I don't know what I'm doing wrong:
componentDidMount(){
this.props.getPeople();
}
_renderItem = ({ item }) => (
<View style={{flex: 1}}>
<Text>{item}</Text>
</View>
);
render(){
const { inProgress, people, error } = this.props.peopleData;
return (
<FlatList
data={people}
renderItem={this._renderItem}
/>
);
}
when console log people this is result:
{cpycwkz7exVBzmhaLEtHjMW66wn1: {…}, UbIITfUANmb63cYE2x7dZRZ0pfK2: {…}}
FlatList component expects its data prop to be an array. You are passing it as an Object. You can change it to an array of Objects. Then too in your _renderItem method the item will be an object and it can't be rendered straight away in <Text>, you have to extract a text value from the item object and than render it as: <Text>SOME_TEXT_NOT_AN_OBJECT</Text>
You can convert your people object to an array and pass it to the <FlatList like this:
render(){
const { inProgress, people, error } = this.props.peopleData;
let ArrayOfPeopleObject = Object.values(people)
return (
<FlatList
data={ArrayOfPeopleObject}
renderItem={this._renderItem}
/>
);
}
Now each item in the _renderItem method will be an object and you can extract value from any key and render it in the <Text>.
Flatlist data requires a key for each object in the array you can convert the firebase result like this:
Object.entries(peopleFromFirebase).map(item => ({...item[1], key: item[0]}));
So json from firebase like this:
{
cpycwkz7exVBzmhaLEtHjMW66wn1: {
name: 'wade owen watts',
phone:'+447...'
},
UbIITfUANmb63cYE2x7dZRZ0pfK2: {
name: 'Helen Harris',
phone:'+448...'
}
}
becomes:
[
{
key: 'cpycwkz7exVBzmhaLEtHjMW66wn1',
name: 'wade owen watts',
phone:'+447...'
},
{
key:'UbIITfUANmb63cYE2x7dZRZ0pfK2',
name: 'Helen Harris',
phone:'+448...'
}
]
Flat list except array of objects but the firebase return the data as map like {key: value} pair so you should transform this map to array , you can install lodash module and use _.values() function to do that

Populate ReactNative ListView with Firebase data

I've been struggling for a few days to get this working. I want to populate a ListView in React Native with data from my Firebase database. I have this setup:
const ListItem = require('./profileRow');
var database = firebase.database();
var userId, dataKey;
class Selection extends Component {
constructor(props) {
super(props);
userId = firebase.auth().currentUser.uid;
this.dataRef = firebase.database().ref('/users/' + userId + '/profiles_info');
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
}
listenForProfiles(dataRef){
dataRef.on('value', (snap) => {
var profiles = [];
snap.forEach((child) => {
profiles.push({
name: child.val().forename,
_key: child.key
});
});
alert(profiles);
this.setState({
dataSource: this.state.dataSource.cloneWithRows(profiles)
});
});
}
componentDidMount() {
this.listenForProfiles(this.dataRef);
}
render() {
return (
<Image source={require('../assets/bg.png')} style={styles.container}>
<View style={{flex: 1, flexDirection:'column'}}>
<Text>
Select a profile to view:
</Text>
</View>
<View style={{flex: 1}}>
<ListView dataSource={this.state.dataSource} renderRow={this._renderItem.bind(this)} enableEmptySections={true} style={styles.listview}> </ListView>
</View>
</Image>
);
}
_renderItem(item) {
return (
<ListItem item={item}/>
);
}
}
The datastructure looks like this:
So what I'm trying to do here is populate each row of the ListView with the "forename" String of each "profile" directory (0,1,2).
But on my alert() I am returned: [object Object],[object Object],[object Object] which must mean it is only fetching the directories as Objects from "profiles_info" and not each "forename" String from those directories.
This is where I am stuck, I hope someone can shed some light on this. I know what the solution should be, I just don't know how to apply it in code.
Thanks in advance!
You are pushing objects into your profiles array here, so it makes sense that your alerts are showing objects:
profiles.push({
name: child.val().forename,
_key: child.key
});
If you use alert(JSON.stringify(profiles)); instead of alert(profiles); you should see an array of objects with fields name and key. Accessing profiles[0].name would give the actual name.
Side note, if you use console.log instead of alert you get some more meaningful information.
Try Following Tutorial. may be help you.
react-native-firebase-tutorial-list

Resources