Reactjs problem rendering binary image retrieved from mongodb - javascript

Hello so I have images in a mongodb database and I'm trying to render them on the client side however It's not working. I convert the buffer unit8 data into base64 so I can render it it seemd to work and been stored in the images state but images are not accessible by associative array.
useEffect( () => {
const getAllDoctors = async () => {
const result = await api.get('doctor/all')
const myImages = []
setDoctors(result.data)
await result.data.forEach(async doctor => {
myImages[doctor._id] = await base64_arraybuffer(doctor.photo.data.data)
})
setImages(myImages)
setLoading(false)
}
getAllDoctors()
}, [])
as for the render
return (
<div>
{
images.map((image, key) => {
console.log(doctors)
return (
<div key={key}>
<img alt={'image'} src={`data:image/png; base64, ${image}`}/>
<div>{`Doctor + ${images}`}</div>
</div>
)
})
}
</div>
);
converter (not mine):
const base64_arraybuffer = async (data) => {
const base64url = await new Promise((r) => {
const reader = new FileReader()
reader.onload = () => r(reader.result)
reader.readAsDataURL(new Blob([data]))
})
return base64url.split(",", 2)[1]
}

Two things
The way your array assignment is doesn't make sense. If _id is a ID string, then you are using a string to key an array, the data assigned at that key won't be included in loops. Use Array.prototype.push to add the item to the next available index.
Array.prototype.forEach and async / await don't play nice together due to the nature of callbacks. Try using a traditional loop.
useEffect(() => {
const getAllDoctors = async () => {
const result = await api.get("doctor/all");
const myImages = [];
setDoctors(result.data);
for (const doctor of result.data) {
const image = await base64_arraybuffer(doctor.photo.data.data);
myImages.push(image)
}
setImages(myImages);
setLoading(false);
};
getAllDoctors();
}, []);

Related

Array of fetched items doesn`t show after data is fetched

I have this code:
const fetchPokemonData = async () => {
const data = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151', options)
let pokemons = []
await data.json().then(pokemon => pokemons.push(pokemon.results))
return pokemons}
const getPokemonsUrl = async () => {
const pokemonsUrls = []
const pokemonData = await fetchPokemonData()
pokemonData[0].map(pokemons => pokemonsUrls.push(pokemons.url))
return pokemonsUrls}
const createPokemonObject = async () => {
const urls = await getPokemonsUrl()
const arrayOfPokemons = []
urls.map(async url =>{
const data = await fetch(url)
const pokemon = await data.json()
const { name, id, sprites: {other: {dream_world: {front_default}}}, types, weight, stats } = pokemon
arrayOfPokemons.push(
{
name: name,
id: id,
image: front_default, types: types,
weight: weight,
stats: stats
}
)
})
console.log(arrayOfPokemons) //works
arrayOfPokemons.map(pokemon => console.log(pokemon)) // works only after setTimeout() delay }
The problem is when I try to log each pokemon outside urls.map() array function, there is no each individual Pokemon, because the data isn`t fetched yet (I tested that put by putting arrayOfPokemons inside setTimeout() function and after some delay time, each Pokemon was shown). Can someone please rewrite or explain the way to rewrite this code do that I get all individual pokemons outside of urls.map() function, because that is the best way for me to learn.
There are several issues:
push returns the length of the array, not data. Instead of using push, really map the data with a .map and capture the returned array.
.map(async will execute the async callbacks immediately and continue. There is no waiting for those callbacks to terminate their asynchronous code. Either use a normal for loop or use await Promise.all
Here is a correction of your code:
const fetchPokemonData = async () => {
const data = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
const pokemon = await data.json();
return pokemon.results;
}
const getPokemonsUrl = async () => {
const pokemons = await fetchPokemonData();
return pokemons.map(pokemon => pokemon.url);
}
const createPokemonObject = async () => {
console.log("wait for it...");
const urls = await getPokemonsUrl();
const arrayOfPokemons = await Promise.all(urls.map(async url => {
const data = await fetch(url);
const pokemon = await data.json();
const { name, id, sprites: {other: {dream_world: {front_default}}}, types, weight, stats } = pokemon;
return {
name: name,
id: id,
image: front_default, types: types,
weight: weight,
stats: stats
};
}));
console.log(arrayOfPokemons);
}
createPokemonObject();

Put results of a Get request from axios to an array

I don't understand a very simple task, I made a request from a API with axios in react.
If I console log the res.data, is like 170 result of single objects on my console.
I need to convert all these result in a single array of objects.
It's a basic task but I don't understand how to do it.
The application is a Trello Clone.
I have a variable called board that has all the data and with this list request, I grab all the column the the trello and append to ListObjects [] in newBoardData (it's a clone of board)
Here is my code:
//Get Request
const getList = async (id) => {
try {
return await axios.get(`${ENDPOINT}/lists/${id}`);
} catch (err) {
console.log(err);
}
};
const [loading, setLoading] = useState(false);
// Use Effect for grab the data with the listId
useEffect(() => {
(async () => {
setLoading(true);
const res = await (getList(listId));
//Loading up the listObjects
const oldList = board.board.listObjects
const newList = []
const payload = res.data;
//Adding all the old values to the new list (except for the current payload id)
for(let obj of oldList){
if(obj._id !== payload._id) newList.push(obj)
}
//Adding the current payload id
newList.push(payload)
const data = {
...board,
board: {...board.board, listObjects: newList}
};
setList(res.data);
// Here I put the data objects with the new ListObjects Array
setBoardNew(data);
setLoading(false);
})();
}, []);
Here is the console log of the get request res.data:
console.log of res.data
here is the board object:
board object
You can saw that there is a spam of result with the current res.data in ListObjects
I'think it make a request for every card in every list.
thank you very much!
UPDATE:
I will explain how the app works:
I have a file called Board.js, where I make this call (in the console log I have two call if I have two columns):
try {
return await axios.get(`${ENDPOINT}/boards/${id}`);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
(async () => {
setLoading(true);
const res = await (getUserBoard(match.params.id));
if (res) {
axios.defaults.headers.common['boardId'] = match.params.id;
} else {
delete axios.defaults.headers.common['boardId'];
}
const payload = { ...res.data, listObjects: [], cardObjects: [] };
const data = {
...state,
board: { ...state.board, ...payload },
};
setBoardData(data);
setLoading(false);
})();
}, []);
Then I send the props data to the file List.js
{board.board.lists.map((listId, index) => (
<List key={listId} listId={listId} index={index} board={board} />
The list file send the data to
card.js
{list.cards.map((cardId, index) => (
<Card key={cardId} cardId={cardId} list={list} index={index} board={boardNew} />
The logic is: There is the board(board.js), in the board there are the lists (column)(list.js) in the lists there are the cards (card.js)
I hope it's more clear.
simple use this approach to add your new id value into the array state.
this.setState({ myArray: [...this.state.myArray, 'new value'] }) //simple value
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] }) //another array

Delete image Doc from firebase firestore

I am trying to delete a document from firebase firestore collection
how can I do that , I tried few things but no success
here is my code
here is the Firestore hook
const useFirestore = (somecollection) => {
const [docs, setDocs] = useState([]);
useEffect (() => {
// new collection reference
const newcoll = collection(projectFirestore, somecollection);
const q = query(newcoll, orderBy('createdAt', 'desc'));
const unsub = onSnapshot(q, (snapshot) => {
let documents = [];
snapshot.forEach(doc => {
documents.push({...doc.data(), id: doc.id})
})
setDocs(documents);
});
return () => unsub;
},[somecollection]);
return { docs };
}
the imagegrid where all the images are shown (only the js without the jsx return)
const { docs } = useFirestore('images');
const handleDelete = (e) => {
docs.forEach(doc => {
deleteDoc(doc);
})
now , on the imagegrid jsx I have all the uploaded images that store the document in them.
so I added a button to every image and I want that when I click the button its fires a handleDelete() that delete this specific image document from the firestore
If you are trying to have a button that deletes a single document, you likely do NOT want to be looping over all of the docs and calling deleteDoc().
Instead, your UI would likely display each image and include its ID for each button. Something like:
const deleteOneImageDoc = async (docId) => {
try {
let docRef = doc(myFirestore, `images/${docId}`);
await deleteDoc(docRef);
} catch (ex) {
console.error(`Delete FAILED: ${ex.message}`);
throw ex;
}
};
return <div>
docs.map((imgDoc) => {
return (
<div key={imgDoc.id}>
<img src={imgDoc.url}/>
<button onClick={() => deleteOneImageDoc(imgDoc.id)}>
delete {imgDoc.id}
</button>
</div>);
})
</div>;

Delete Function using .filter Method

I am creating a note taking app and am getting stuck on my deleteNote function. I am needing to use the .filter method to remove notes w/ an assigned id and keep the ones not associated to that id. Here is my code so far.
const fs = require("fs");
const util = require("util");
// returns a unique ID for our returns
const uuidv1 = require("uuid/v1");
// creates a promified version of fs.readfile and writefile
const readFileAsync = util.promisify(fs.readfile);
const writeFileAsync = util.promisify(fs.writefile);
class Store {
read() {
return readFileAsync("./db/db.json", "utf8");
}
write(note) {
return writeFileAsync("./db/db.json", JSON.stringify(note));
}
getNotes() {
return this.read().then(notes => {
let parsedNotes;
try {parsedNotes = [].concat(JSON.parse(notes))}
catch (err) {
parsedNotes = [];
}
return parsedNotes;
})
}
// example of destructuring -- im taking the title, text string and destructuring it to add a unique id.
addNote(note) {
const {title, text} = note;
const newNote = {title, text, id: uuidv1()};
return this.getNotes()
.then(notes => [...notes, newNote]);
.then(updatedNotes => this.write(updatedNotes));
.then(() => newNote);
}
// delete note is going to take in an id and use a .filter to only keep the notes that do not include that id.
deleteNote() {
}
}
module.exports = new Store();
You already know you're filtering, so just do it:
deleteNote(id) {
return this.getNotes()
.then(notes => notes.filter(note => note.id !== id))
.then(updatedNotes => this.write(updatedNotes));
}
Try this one which includes returning deleted note.
async deleteNote(id) {
const notes = await this.getNotes();
const filtered = notes.filter((note) => note.id !== id);
await this.write(filtered);
const deleted = notes.filter((note) => note.id === id);
return deleted;
}

upload multiple files to firebase storage issues

i am working on a cooking android app where firebase is the backend, i need to upload multiple images of a recipe in firebase stoarge and then store the downloadurl into firebase database.
i managed to upload the files into firebase but i am having some trouble to get the downloadUrl of these files.
i have created an array of promises to upload the files and i had created another array to store the url of each file which i get when it finishes the uploading task.
here is my code
var promises = [];
for (var i=0 ;i< prepaImages.length;i++)
{
//alert(prepaImages[i].name);
var storageRef = firebase.storage().ref("receipes"+"/"+category+"/"+title+"/"+uploadTime+prepaImages[i].name );
var uploadTask = storageRef.put(prepaImages[i]);
promises.push(uploadTask);
uploadTask.on('state_changed', snapshot => {
var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
$("#prepaImageUploadprogress").html(Math.round(percentage)+"%");
$("#prepaImageUploadprogress").attr("style", "width:"+percentage+"%");
}, error => { alert(error) }, () => {
uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
//prepaImagesUrl+="+"+downloadURL;
prepaImagesUrl.push(downloadURL);
});
});
the problem is i am getting an array of the length of the number of uploaded files minus one (the legnth it should be equal to the number of uploaded files) and it has the same value (the same downloadurl)
. any help will be appreciated
Thank you.
I think the problem is with the promisies. I suggest you to use Promise.all and await. Therefore your code will be more reliable. Here is my solution to multiple file upload (adapt to your variable names):
const array = Array.from({ length: prepaImages.length }, (value, index) => index);
const uploadedImages = await Promise.all(array.map(async index => {
const image = prepaImages[index];
const metadata = { contentType: image.type };
const storageRef = firebase.storage().ref(`receipes/${category}/${title}/${uploadTime}${prepaImages[i].name}`);
const uploadTask = storageRef.put(image, metadata);
const url = await new Promise((resolve, reject) => {
uploadTask.on('state_changed', snapshot => {
const percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
$('#prepaImageUploadprogress').html(`${Math.round(percentage)}%`);
$('#prepaImageUploadprogress').attr('style', `width: ${percentage}%`);
}, error => reject(error),
async () => {
const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
resolve(downloadUrl);
});
});
return { name: image.name, url };
}));
The uploadedImages will contains an array with the image names and download urls. You can make this without await of course, but I prefer this way.
UPDATE:
Here is my own code (without error handling) to achieve this, also, I need to mention that I'm using this with react, redux and using the firebase, firestore wrapper for redux redux-firestore and react-redux-firebase but these are just wrappers:
export const addNewWork = work => async (dispatch, getState, { getFirebase, getFirestore }) => {
const { files, ...restWork } = work;
const firebase = getFirebase();
const firestore = getFirestore();
const storageRef = firebase.storage().ref();
const array = Array.from({ length: files.length }, (value, index) => index);
const uploadedFiles = await Promise.all(array.map(async index => {
const file = files[index];
const metadata = { contentType: file.type };
const uploadTask = storageRef.child(`works/${file.name}`).put(file, metadata);
const url = await new Promise((resolve, reject) => {
uploadTask.on('state_changed', () => {}, error => reject(error), async () => {
const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
resolve(downloadUrl);
});
});
return { name: file.name, url };
}));
await firestore.collection('works').add({
...restWork,
image: uploadedFiles[0], // Use only one image for the clean example
createdAt: new Date()
});
});

Categories

Resources