Best practice for using Firebase Reference - javascript

The function below is called to create a post
const createPost = (newPost) => {
app.database().ref('posts').push(newPost);
}
This is another version of the function
const postRef = app.database().ref('posts');
const createPost = (newPost) => {
postRef.push(newPost);
}
Which is preferable and why

Both will add data to the database, but the second is better:
const postRef = app.database().ref('posts');
const createPost = (newPost) => {
postRef.push(newPost);
}
Since postRef is referring to a root node in the database which you might use later in the js file.

push returns a promise which resolves when the write to the database is complete. If you don't wait for it to resolve you will not know if it fails as your function will have returned successfully. Instead you will get an Unhandled rejection. Also, anything that runs after the functions has returned will receive less CPU and memory in firebase. So rewrite it like this:
const createPost = async (newPost) => {
await app.database().ref('posts').push(newPost);
}
As to your original question you should not declare a variable if only used once, so I would opt for the first variation.

Related

Can't access lexical definition of response before initialization within a React hook

I'm trying to make the home page send one API call on load and display all results on screen. It seems to send the call and receive response fine, although despite receiving the response from the server it can't pass the contents of the payload within the code, which is a JSON.
useEffect(() => {
const localUser = localStorage.getItem("user");
if (localUser) {
const foundUser = localUser;
setUser(foundUser);
} else {
const newUser = uuidv1();
localStorage.setItem(newUser, user);
setUser(newUser);
}
console.log(user);
async function fetchPosts() {
try {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
.then(response.payload.forEach(object => tempPosts.push(object.post)))
.then(setPosts((posts) => [tempPosts]));
console.log(posts);
} catch (err) {
console.log(err)
}
}
fetchPosts();
}, [user, posts]);
Somehow React is trying to access the response without the declaration and I have no idea how, which in result stops the function from executing.
Take a look at this line:
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
You're combining two paradigms - asynchronous programming using Promises and callbacks with then, and asynchronous programming using async/await. You'll usually want to pick one or the other for use in a single function, and you definitely cannot combine them in a single line (or at least, not like this).
If you want to use async (and I would recommend this approach), you'll probably want something like this:
async function fetchPosts() {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts');
const data = await response.json();
data.payload.forEach(object => tempPosts.push(object.post))
return tempPosts;
}
I don't know what data looks like, so you may have to play with the 4th line of the function, but hopefully you get the gist. You'll probably want to define this function outside of your component, and certainly not within the useEffect hook.
An example of how you could use this to fetch posts on the first render of your component is
useEffect(() => {
fetchPosts().then(data => setPosts(data));
}, []);
assuming you have a relevant useState hook.

Action is being dispatched before the api call so payload is being send as promise

import {fetchData} from "../common/helpers";
import {updateData} from "../Data/ducks/actions"
fetchData will check for is data available in local storage or not otherwise it will make an api call to get the data.
class Data {
dataCheck(id) {
if (isDataAvailable) {
let data;
const fetchEligibleData = async () => {
let dataAvailabilityCheck = fetchData("DATA_EMPLOYEE");
data = dataAvailabilityCheck;
if (dataAvailabilityCheck instanceof Promise) {
data = await dataAvailabilityCheck;
}
return data;
}
const eligibleData = fetchEligibleData();
Store.dispatch(updateData({ availableData: eligibleData }))
}
}
}
export default new Data
in the above code the important part starts from if(isDataAvailable).
payload for the updateData action is always being sent as promise. this is not expected behaviour.
first promise should be resolved then the data will be passed to the updateData action.
Please suggest me the approach to solve the promise before store action being triggered.
Set variable data to the result of the promise if it is successful. I.E. fetch(APIURL).then(res => data = res);
Or, fetch(APIURL).then(async res => data = await res.JSON());
I realized your api call is elsewhere, and it looks like you're querying it for specific 'Employee' info. Without seeing that, I'm not sure what you're doing there. I don't know that you should be awaiting a promise at that point, but if you are, you still will need to get the result of it.

Can someone help me understand how async + await + useEffect work in React?

I have a React app built with the Minimal template and I'm trying to follow along with one of their tutorials, in order to create a Redux slice that feeds some data to a custom component. The data itself is collected from Firebase. Below is my code:
firebase.js - helper
export function getDocuments(col) {
const colRef = collection(db, col);
const q = query(colRef, where('uid', '==', auth.currentUser.uid));
getDocs(q).then((snap) => {
const data = snap.docs.map((d) => ({ id: d.id, ...d.data() }));
return data;
});
// return [1,2,3]
}
product.js - Redux slice
export function getProducts() {
return async (dispatch) => {
dispatch(slice.actions.startLoading());
try {
const products = await getDocuments('products');
dispatch(slice.actions.getProductsSuccess(products));
} catch (error) {
dispatch(slice.actions.hasError(error));
}
};
}
ProductList.js - component
const dispatch = useDispatch();
const { products } = useSelector((state) => state.client);
useEffect(() => {
dispatch(getProducts());
}, [dispatch]);
useEffect(() => {
if (products.length) {
// setTableData(products);
}
}, [products]);
If I console log data in the helper function (firebase.js), I get the values I expect, once the promise is resolved/fulfilled. However, if I console.log clients in the product.js slice or later in the component, I get undefined.
I assume my problem is not being able to understand how async + await + useEffect work together in order to fix this. My assumption is that I am trying to access the value before the promise is resolved and therefore before the helper function returns it. I confirmed that by returning a simple array [1, 2, 3] in my helper function as a test.
I think I am missing something fundamental here (I am not very experienced with React and JS in general and still learning things on the go). Can someone help me understand what am I doing wrong?
Thank you!
With await you can await the fulfillment or rejection of a promise, but your getDocuments Function does not return a promise. Change the last line of the function to the following:
return getDocs(q).then((snap) => {
const data = snap.docs.map((d) => ({ id: d.id, ...d.data() }));
return data;
});
Async and Await are no different in React than in plain JavaScript:
When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method
useEffect():
By using this Hook, you tell React that your component needs to do something after rendering. This function will run every time the component is re-rendered.

API call in useEffect returning undefined

I have a simple functional component, which includes a fetch to a JSON file stored locally.
I am trying to bring in the data from the JSON file, and whilst this should be a simple task, I have come across a problem.
My console log is showing two separate logs - one is an empty object, presumably from the useState definition and the second has the data from the fetch inside it.
Therefore, When I try to do anything with the data I'm fetching, undefined is being returned, So I can't use it. I feel like I'm missing something obvious, but I'm not entirely sure what is going on here. I have tried async/await without success.
What am I missing ?
const Landing = () => {
const [api, updateApi] = useState({});
const getData = () => {
fetch('data.json')
.then((response) => response.json())
.then((data) => updateApi({api: {data}}))
}
useEffect(() => {
getData();
}, []);
console.log(api)
return (
<p>Hey!</p>
)
}
All you need to do is to wrap the return within an if/else block, and return (for example a spinning circle) loading indicator. When it re-renders with the data from the api, it returns the desired representation of the data.
Using async/await syntax you can put your fetch requests within the useEffect hook and set your state similar to how you are currently doing.
export default function Landing() {
const [api, updateApi] = useState({});
useEffect(() => {
const getData = async () => {
const response = await fetch("data.json");
const data = await response.json();
updateApi({ api: { data } });
};
getData();
}, []);
return <div>{JSON.stringify(api)}</div>;
}
If you initialise the state with useState({}) then the value won't be undefined. If however you want to check for that before doing something then an if/else statement as suggested in another answer is suitable or if it is within the component return then a common pattern is return <div>{api && JSON.stringify(api)}</div>;.

Not sure how to handle this in react - multiple promises

So the easiest way to explain this is that I am trying to search your package.json for all your packages, then use the npm registry to see if there are new updates. I have most of that done. Accept for the following for loop:
import request from 'request'
export const findAllUpdates = (packageInfo) => {
for(prop in packageInfo) {
request('https://registry.npmjs.org/' + prop, function(err, resp) {
json = JSON.parse(resp.body);
if (json['dist-tags'].latest !== packageInfo[prop].version) {
// Do something, we are not the same version so we should
// update some counter.
}
});
}
}
Your packageInfo is a object of key=>value which represents either a dependency or a dev dependency from the package.json and the package-lock.json or the yarn.lock
The important part is we look at, in the above function, what you have installed, we then use the registry to get that package info and compare the latest version on the registry to that of what you have installed and then we want, ideally, to update the state of the component with the total count.
how ever the issue is that we have entered callback hell, especially with a for loop, looping over every package, making a request.
I cannot create a variable and store the response in there because it cannot be accessed after the request is made. I cannot see how using events here would work because you might have 40 packages installed as either dep or dev and thats a lot of events to fire.
And finally the proper solution might be to use promises, but the whole concept of promises is yet more callbacks with the .then(() => {}), .catch(() => {}) which puts me back at square one.
The goal is to call this in a component and have the state of that component be updated with the total amount of packages that have to be updated (or at least have new versions)
Any ideas? Am I going about this all wrong?
You're right to be looking at promises.
The first thing is to give yourself a promise-enabled version of request. There's a promisify function in util that takes a Node-callback-style function and returns a promise-enabled version. So:
import util from 'util';
// ...
const prequest = util.promisify(request);
(There's also an npm module, promisify, that can do entire APIs all at once.)
Then, do your requests, gathering the promises in an array (and using then on them to do any post-processing; then returns a new promise, so we're still good), and use Promise.all to wait until they all resolve (or any of them rejects);
So all together:
import request from 'request';
import util from 'util';
const prequest = util.promisify(request);
export const findAllUpdates = (packageInfo) => {
const updates = []
for (const prop in packageInfo) {
updates.push(prequest('https://registry.npmjs.org/' + prop).then(resp => {
const json = JSON.parse(resp.body);
if (json['dist-tags'].latest !== packageInfo[prop].version) {
// Do something, we are not the same version so we should
// update some counter.
}
// Perhaps return something here, it will be the resolution
// for the promise for this request; otherwise, the promise
// will resolve with `undefined`.
}));
}
return Promise.all(updates);
};
That overall function's promise will resolve to an array of the results of each promise in the array (in order), or (again) reject if any of them rejects.
I want to be able to suggest async/await, but it doesn't currently bring much to the table for a bunch of parallel promises. (There's been the occasional talk of await.all to do what Promise.all does, but it hasn't caught on [yet?].)
Having said that, though, if we break things up a bit it is a bit nicer with async/await:
import request from 'request';
import util from 'util';
const prequest = util.promisify(request);
const checkOne = async (prop) => {
const resp = await prequest('https://registry.npmjs.org/' + prop);
const json = JSON.parse(resp.body);
if (json['dist-tags'].latest !== packageInfo[prop].version) {
// Do something, we are not the same version so we should
// update some counter.
}
// Perhaps return something here, it will be the resolution
// for the promise for this request; otherwise, the promise
// will resolve with `undefined`.
};
export const findAllUpdates = (packageInfo) => {
const updates = []
for (const prop in packageInfo) {
updates.push(checkOne(prop);
}
return Promise.all(updates);
};
And of course, if all the properties in packageInfo are "own" properties (not inherited), findAllUpdates gets a lot simpler:
export const findAllUpdates = (packageInfo) => {
return Promise.all(Object.keys(packageInfo).map(checkOne));
};
Side note: I've added a few missing declarations above.
I would recommend using Promise. You can make several async calls, and then wait for them using Promise.all.
If you also return a Promise from your "findAllUpdates" method, then it is easy for the caller to just update its own state when the information is available.
In React that would be something like:
findAllUpdates(..)
.then(result => this.setState({
packagesNeedUpdate: result.filter(p => !p.latest).length
}));
I've created a simple example below that uses promises. It fakes the requests, but otherwise should be pretty accurate.
// Fake requests. Will return version '1.0.0' after one second.
const makeRequest = url => {
return new Promise(resolve => {
setTimeout(() => {
resolve(JSON.stringify({
'dist-tags': {
'latest': '1.0.0'
}
}, null, 2));
}, 1000);
});
}
const findAllUpdates = (packageInfo) => {
return new Promise(resolve => {
const promises = Object.keys(packageInfo)
.map(prop => makeRequest('https://registry.npmjs.org/' + prop).then(JSON.parse));
Promise.all(promises) // Wait for all promises
.then(jsons => {
const results = Object.keys(packageInfo)
.map((prop, i) => {
const latestInNpm = jsons[i]['dist-tags'].latest;
const current = packageInfo[prop].version
return {
prop,
latest: latestInNpm === current
}
});
resolve(results); // Return result to caller
});
});
}
function run() {
console.log('Loading..');
const packages = {
'react': {version: '0.0.1'},
'angular': {version: '1.0.0'},
'ember': {version: '0.5.0'},
'mithril': {version: '0.9.0'}
};
findAllUpdates(packages).then(result => {
const needUpdates = result.filter(p => !p.latest).length;
console.log('Need updates: ', needUpdates);
});
}
run();

Categories

Resources