I have a direct messaging application. All the data is stored in Firebase. Each chat contains an array of user IDs.
I use the following function to get all chats from componentDidMount():
return dispatch => new Promise(resolve => FirebaseRef.child('chats')
.on('value', snapshot => resolve(dispatch({
type: 'CHATS_REPLACE',
data: snapshot.val() || [],
})))).catch(e => console.log(e));
Which goes through:
chatReducer(state = initialState, action) {
case 'CHATS_REPLACE': {
let chats = [];
if (action.data && typeof action.data === 'object') {
chats = Object.values(action.data).map(item => ({
id: item.id,
title: item.title,
authorizedUsers: Object.values(item.authorizedUsers).map(user => ({
id: user.id,
// Somedata: fetchUserData(user.id)
// -> pretty sure it can't be done here <-
})),
}));
}
return {
...state,
error: null,
loading: false,
chats,
};
How would I go about fetching more data of every user inside each chat from Firebase at users/:uid?
I don't know what is the use case of this. It would be great if you can share, like how much information about the user you want to use. If its small data, why don't you add it in same API Only. You can pass the users data in the same object with user id as keys, and use the same keys inside your nested data like (only if user data is small or you know API data is always limited like because of pagination or page size. :
{
posts : [
{
title : 'abc'
authorizedUsers : ['1a', '2b', '3c']
}, ....
],
users : {
'1a' : {
name : 'john doe',
profileImage : 'https://some.sample.link',
},
'2b' : {
name : 'bob marshal',
profileImage : 'https://some.sample.link2',
}
}
}
If data is huge or cannot be added in the API ( because API is owned by 3rd party), then only place you can put you code is, instead of just dispatching the actions after the response is recieved, loop over the response in your service only, make async calls to get all "Unique users" only, append that data to the data you recieved from the previous api call, and then dispatch the action with the complete data to the store. It might not be the best way, as everything will have to stall i.e. even the data recieved in 1st api also will stall(not updated on screen) till all the users data is fetched. But best solution can only be given once we know more details about the use case. Like maybe lazy fetching the users data as end user scrolls the screen and may see a particular post Or fetching the user details once you start rendering your data from 1st API call like making a component for showing user associate with a post and in its componentDidMount, you pass the userIds as props from top component which might be "article/post/blog" component and it fetched the data at the time when it is actually rendering that "article/blog/post".
Hope this helps.
Related
I have two TaskList components that use the same query GET_TASKS.
Both use a different filter query variable which is passed down to them in props as queryVars.
I defined a standard merge function in type policies to merge the incoming and existing data together.
The TaskList component uses
const { data, fetchMore } = useQuery<Response, Variables>(GET_TASKS, { variables: queryVars })
to retrieve the data.
A Fetch more button has () => fetchMore({ variables: queryVars }) in the onClick attribute.
When I click on the Fetch more button on the left, the tasks on the right get updated as well, however, without its filter applied, so the data that come with Assigned to me filter are also put to the Assigned by me task list and vice versa.
The merge function basically rewrites every data object that uses the given query.
How do I tell Apollo to only update the data that is bound to the component where fetchMore is defined?
You should be able to add filter to the keyArgs property. This should create different cache results based on the filter.
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
tasks: {
keyArgs: ["filter"],
merge(existing, incoming, { args: { offset = 0 }}) {
//Custom merge
},
},
},
},
},
});
I am running into a problem where when I submit a "property listing" I get this response:
{"owner_id":"Batman","address":"test","state":"test","sale_price":"test"}
The thing is "owner_id" is supposed to equal or associate with owner's id in a different table/JSON file (e.g owner_id = owner.id), not a string in this case which is why the object is not saving on the back-end.
Is anyone in vanilla JavaScript able to show me an example on how to associate owner_id and owner.id?
It'd be more like :
{
owner: {
id: "Batman"
},
address: "test",
state: "test",
sale_price: "test"
}
You should take a look at : https://www.w3schools.com/js/js_json_objects.asp
EDIT: Not sure how you're fetching this data but it seems like you want to handle the response you're getting.
Here is a simple GET request using the fetch api:
fetch('http://example.com/heroes') //this is the path to your source where you're getting your response data
.then((response) => {
return response.json();
//above you return a promise containing your response data
//you can also handle your response before declaring it in a var here
})
.then((myJson) => {
//you have stored your response data as a var myJson
console.log(myJson);
//here you can work your response data in any way you need
// to show an example (not that you would do this) I've provided a owner object that checks if it's property is equal to the incoming data
var owner = {
"id": Batman,
}
if ( myJson.owner_id === owner.id ) {
//do something here
}
});
More info here.
Pop quiz, hotshot:
You're building a react native app. You set some values to firebase as an object at the root of your app, like this:
firebase
.database()
.ref("/companies/")
.set({
awesomeCompany: {
name: "Awesome Company",
owner: "Joe Awesome",
gmail: "joeawesome#gmail.com",
fleetSize: 2
},
badCompany: {
name: "Bad Company",
owner: "Joe Bad",
gmail: "joebad#gmail.com",
fleetSize: 3
}
You want to give the current user a text input field through which they may change the fleetSize of a company if they are the owner of that company.
You have your firebase auth working properly, so you know that firebase.auth().currentUser.email will work to check against to determine if they are an owner.
Your database values have been set - they look like this:
{
"companies": {
"awesomeCompany": {
"fleetSize": 2,
"gmail": "joeawesome#gmail.com",
"name": "Awesome Company",
"owner": "Joe Awesome"
},
"badCompany": {
"fleetSize": 3,
"gmail": "joebad#gmail.com",
"name": "Bad Company",
"owner": "Joe Bad"
}
}
}
How would you render the initial information to the screen, and how would you set up the text input logic so that the user input changes data at the database?
To understand the brain I have, and how I'm failing, I'm including my own code below as a starting point. If there's a way to show me how I could take my basic strategy and make it work - even if it isn't elegant - I'd appreciate that. But overall I'm just really struggling with how to get data path references using Data Snapshot and keep them available to use elsewhere.
Thanks for your help, anyone!
// my crummy half baked code below
import React, { Component } from "react";
import { View, Text, TextInput, Button } from "react-native";
import { styles } from "../styles";
import * as firebase from "firebase";
export default class OwnerProfileScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
gmail: null,
name: null,
fleetSize: null
};
}
componentDidMount() {
this.getData();
}
getData = () => {
const rootRef = firebase.database().ref(); // firebase reference
const authEmail = firebase.auth().currentUser.email; // current user
return rootRef.once("value").then(
function(snapshot) {
const idArray = Object.keys(snapshot.child("companies/").val()); // array of Ids
const companyData = idArray.map(id =>
snapshot.child("companies/" + id).val()
); // values of contained in objects at each key
const ownersCompany = companyData.filter(
obj => obj.gmail === authEmail
); // an array containing one object if the gmail address in the object is the same as the currentUser logged in
// what is the path of fleetSize?
// how do I define it to keep it available to use later
// with a Text Input event?
this.setState({
name: ownersCompany[0].name,
gmail: ownersCompany[0].gmail,
fleetSize: ownersCompany[0].fleetSize
});
}.bind(this)
);
};
changeFleetSize = userInput => {
//in order to set the user input to the database, I need the path
//of the fleetSize of the current user (who has been verified as an
// owner by comparing firebase auth to gmail addresses of company)
};
render() {
return (
<View style={styles.container}>
<Text>minPrice = {this.state.name}</Text>
<Text>gmail = {this.state.gmail}</Text>
<Text>fleetSize = {this.state.fleetSize}</Text>
<TextInput
style={{ height: 40, borderColor: "gray", borderWidth: 1 }}
//onChangeText currently does nothing since I don't know how
// to get the particular path of particular fleetSize
onChangeText={userInput => this.changeFleetSize(userInput)}
/>
</View>
);
}
}
The code is quite messy so it's hard to say what you're trying to accomplish. But let me make a guess here:
You want to load a single company from your database.
You know the email address of the owner of the company.
If that is correct, you can use a query to accomplish the goal. Something like:
var query = rootRef.child("companies").orderByChild("gmail").equalTo(authEmail);
var self = this;
query.once("value").then(function(result) {
result.forEach(function(snapshot) { // loop over result snapshots, since there may be multiple
const companyData = snapshot.val();
self.setState({
name: companyData.name,
gmail: companyData.gmail,
fleetSize: companyData.fleetSize
});
})
);
The changes here:
Use a query to only select the companies with the correct gmail address.
Loop over the results, since (on an API level at least) there could be multiple companies with that value for their gmail property.
Get rid of the whole iterating over Object.keys and filtering, since that made it hard to read. This result is also more idiomatic for Firebase Realtime Database code.
Use self to track the this instance, just because I didn't feel like counting bind calls.
Using Redux Form I'm able to retrieve the values which I enter in username. I'm able to filter out the corresponding values, but I need to display my filtered values in the browser.
console.log("pilot.name--->", pilot.name);
Can you tell me how to do it? I provided my code snippet and sandbox below. My related code is in showResults.js: https://codesandbox.io/s/xl1r14w854.
var pilots = [
{
id: 2,
name: "Wedge Antilles",
faction: "Rebels"
},
{
id: 8,
name: "Ciena Ree",
faction: "Empire"
},
{
id: 40,
name: "Iden Versio",
faction: "Empire"
},
{
id: 66,
name: "Thane Kyrell",
faction: "Rebels"
}
];
var rebels = pilots.filter(function(pilot) {
// return pilot.faction === "Rebels";
// return pilot.faction === values.username;
if (pilot.faction === values.username) {
console.log("pilot.name--->", pilot.name);
}
});
I suggest to use a separate reducer for this. Imagine that you call that reducer PilotsReducer where you have your list of pilots in the state. That reducer is "listening" to a specific action like loginSubmitted that you trigger when you get the response from the server.
The payload of that action will be the username that you use to do the filter. In the reducer you can then do the filter and set a state property (e.g. rebels) to the result of the filter. Your component can then be attached to redux to pick the rebels property and it will re-render when that property changes.
This is a typical flow of react/redux, it is not specific to your example
On a side note you can improve it by using reselect so you would keep the entire list of pilots in the state, and filter them in a selector by passing the current username to it. Please check the docs and tutorials of redux and reselect to have a clear view of the entire workflow.
What I am trying to do
I am creating a social media app with react native and firebase. I am trying to call a function, and have that function return a list of posts from off of my server.
Problem
Using the return method on a firebase query gives me a hard to use object array:
Array [
Object {
"-L2mDBZ6gqY6ANJD6rg1": Object {
//...
},
},
]
I don't like how there is an object inside of an object, and the whole thing is very hard to work with. I created a list inside my app and named it items, and when pushing all of the values to that, I got a much easier to work with object:
Array [
Object {
//...
"key": "-L2mDBZ6gqY6ANJD6rg1",
},
]
This object is also a lot nicer to use because the key is not the name of the object, but inside of it.
I would just return the array I made, but that returns as undefined.
My question
In a function, how can I return an array I created using a firebase query? (to get the objects of an array)
My Code
runQ(group){
var items = [];
//I am returning the entire firebase query...
return firebase.database().ref('posts/'+group).orderByKey().once ('value', (snap) => {
snap.forEach ( (child) => {
items.push({
//post contents
});
});
console.log(items)
//... but all I want to return is the items array. This returns undefined though.
})
}
Please let me know if I'm getting your question correctly. So, the posts table in database looks like this right now:
And you want to return these posts in this manner:
[
{
"key": "-L1ELDwqJqm17iBI4UZu",
"message": "post 1"
},
{
"key": "-L1ELOuuf9hOdydnI3HU",
"message": "post 2"
},
{
"key": "-L1ELqFi7X9lm6ssOd5d",
"message": "post 3"
},
{
"key": "-L1EMH-Co64-RAQ1-AvU",
"message": "post 4"
}
...
]
Is this correct? If so, here's what you're suppose to do:
var items = [];
firebase.database().ref('posts').orderByKey().once('value', (snapshot) => {
snapshot.forEach((child) => {
// 'key' might not be a part of the post, if you do want to
// include the key as well, then use this code instead
//
// const post = child.val();
// const key = child.key;
// items.push({ ...post, key });
//
// Otherwise, the following line is enough
items.push(child.val());
});
// Then, do something with the 'items' array here
})
.catch(() => { });
Off the topics here: I see that you're using firebase.database().... to fetch posts from the database, are you using cloud functions or you're fetching those posts in your App, using users' devices to do so? If it's the latter, you probably would rather use cloud functions and pagination to fetch posts, mainly because of 2 reasons:
There might be too many posts to fetch at one time
This causes security issues, because you're allowing every device to connect to your database (you'd have to come up with real good security rules to keep your database safe)