undefined when retrieving e.target.File when uploading file - javascript

I'm trying to obtain the File value within this stack trace
however im getting an undefined when calling it like
console.log(e.target.File);
or
console.log(e.target)
How would i access the file value ?
handleUpload = (e) => {
const data = new FormData()
console.log(e.target);
// data.append('file', e.target.files[0])
// data.append('name', 'some value user types')
// data.append('description', 'some value user types')
// Axios.post('/images/upload', data).then((response) => {
// console.log(response);
debugger;
// this.setState({
// imageUrl: response.data.fileUrl
// })
render(){
return(
.......
<ImageUploader
withIcon={true}
withPreview={true}
buttonText='Upload an image'
imgExtension={['.jpg', '.gif', '.png', '.gif']}
onChange={this.handleUpload}
maxFileSize={5242880}
/>
}

It appears that you are using a React ImageUploader component that is already handling extracting your files from the event. The e in your handleUpload isn't an event rather than the extracted file list. You should work with this file list directly.
You can look into the ImageUploader source to see how it is handling it if you would like to better understand, but accessing the files through e[0] is fine (you may want to rename e to something like files for clarity though).

We don't know the implementation of ImageUploader but from the screenshot it seems that it calls the onChange method with an array of files. So you can access them like that:
handleImagesChange = images => {
const firstImage = images[0];
console.log(firstImage.name);
this.setState({ images });
}
handleSubmit = () => {
Axios.post('some/url', this.state.images)
.then(console.log)
}

Related

File object converts to fakepath when saving inside an object

I've a image input in my webpage and input's output (File object) is saved inside the Question class. questionArr is a array of Question objects
let questionsArr = []; // Array of Question
class Question {
constructor(id) {
this.id = id;
this.image = false;
}
}
when the input value of image input changes, following function calls.
const handleImages = evt => {
let id = evt.target.id; // quizCoverImg or a integer (0,1,...)
const file = evt.target.files[0];
if (file && file.type.startsWith("image/")) {
if (id == "quizCoverImg") {
coverImage = file; // declared in top of the code
// console.log(coverImage) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
} else {
questionsArr[id].image = file;
// console.log(questionsArr[id].image) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
}
}
};
To this point everything works fine. Problem arise when I use above variables somewhere eles
const somewhereElse = () => {
console.log(coverImage); // File {name: "cat.png", lastModified ...} ✔
console.log(typeof coverImage); // object ✔
console.log(questionsArr[0].image); // C:\fakepath\cat.jpg ❓ should return a file object as mentioned above
console.log(typeof questionsArr[0].image); // string ❓
}
I know FileReader() exist, but I want to figure out why I'm getting two different outputs here.
Issue occurred in svelte#3.22.2
Edit 1: Places where questionArr used
This add Question to array
const addQuestion = () => {
const q = new Question(n);
questionsArr = [...questionsArr, q]; // because I'm using svelte :)
n++;
};
Then used in above handleImage()
The key difference is in the toString() method that affects what you are looking at. There is not much context to help debug the details of exactly how you are running this and how you are inspecting the values.
When you have selected a file in a form file input, the browser converts the path to a "fakepath" so that while the script can access the selected file, it cannot learn about the user's directory structure. The filename/path is a reasonable default toString result when trying to inspect/print the file object.

Array is sometimes recreated, and in other times modified

I am trying to implement a multiple image file selector in my React app.
This is my input element:
<input
type="file"
multiple
onChange={handleImageChange}
/>
{renderPhotos(venueImages)}
These are the functions that are called when files are chosen:
const [venueImages, setVenueImages] = useState([]);`
const renderPhotos = source => {
console.log(source); ////////log 1
return source.map(photo => {
return <img src={photo} key={photo} />;
});
};
const handleImageChange = e => {
if (e.target.files) {
const filesArray = Array.from(e.target.files);
console.log(filesArray); ///////// log 2
filesArray.forEach(file => {
const tempUrl = URL.createObjectURL(file);
console.log(tempUrl); ////// log 3
setVenueImages([...venueImages, tempUrl]);
});
}
};
I call renderPhotos to show a preview off all the selected photos before uploading.
The issue I'm facing is as follow:
If I choose, for example, 5 photos, only 1 would end up being rendered on screen.
I've inserted console logs in handleImageChange, and what I get logged is confusing me even more.
The second log (I've numbered them in my code) prints an array of 5 files.
After from log 3 that I'll get 5 logs of the newly generated temporary URLs for each of the files.
But log 1, would only get printed once.
Now - if I'll click the input element to choose more files, I'll end up with another rendered image.
So basically everytime I choose images, no matter how many I've chosen, I'll only get one more image rendered.
The problem is that you are referencing the venueImages array in your setVenueImages call. Because the state update performed by setVenueImages is asynchronous, then you cannot be certain that venueImages contains all of the previous state updates.
The set state function can be passed a function that takes the old value, instead of just passing it the new value. This will ensure that your state updates are sequential. Replace your setVenueImages call with this:
setVenueImages(prevImages => [...prevImages, tempUrl]);
An additional change that I will suggest is to perform a concatenation of all images, instead of adding them one by one. This should be faster.
const handleImageChange = e => {
if (e.target.files) {
const filesArray = Array.from(e.target.files).map(file => URL.createObjectURL(file));
console.log(filesArray); ///////// log 2
setVenueImages(prevImages => prevImages.concat(filesArray));
}
};
That is happening because when you are saving the tempUrl, only one url is getting saved. Also do not set the state by adding images one by one.
Updated version of your handleImageChange function can be
const handleImageChange = e => {
if (e.target.files) {
const filesArray = Array.from(e.target.files);
const tempUrls = filesArray.map(file => URL.createObjectURL(file)))
setVenueImages([...venueImages, ...tempUrls])
}
};

Query into firebase realtime database through functions

I am using firebase functions and Admin SDK to implement a functions on a event trigger. I am trying to push a notification to user when a new update is available. So, when the update version changes from the current, I want to make some changes in the user object and set update_available key as true
Now, the location of event(update_version) that is being tracked and the data to be changed are in to completely different objects. The Object model is as follows:
|root
|- app_info
|- version
|- users
|- <uid>
|- checker
|- update_available: true
Not so far I have achieved this :
function setUpdateValueTrue() {
db.ref().orderByChild("checker/update_available").equalTo("false").once('value', (snapshot) => {
snapshot.forEach((childSnapshot) => {
console.log("got in here");
console.log(childSnapshot.val())
});
})
}
I guess this maybe completely wrong. At the moment i feel stuck. Any help is really appreciated. My major concern is how to I bypass the uid or query through it.
The following should work (not tested however).
Note the use of Promise.all(), since you need to update a variable number of users nodes in parallel.
exports.versionUpdate = functions.database.ref('/app_info').onUpdate((change, context) => {
const beforeData = change.before.val(); // data before the update
const afterData = change.after.val(); // data after the update
if (beforeData.version !== afterData.version) {
const promises = [];
const usersRef = admin.database().ref('/users');
return usersRef.once('value', function(snapshot) {
snapshot.forEach(childSnapshot => {
const uid = childSnapshot.key;
promises.push(usersRef.child(uid + '/checker/update_available').set(true));
});
return Promise.all(promises);
});
} else {
return null;
}
});

How to refactor a function that checks if image (used as css background mage) does exist to return a boolean?

I am working on a react file-upload component. I got stuck with a rather trivial issue – I want for each file to show icon corresponding to a file extension. Icons are loaded via css as background images (using inline styles). The problem arises when I don't have an icon for given extension and thus want to show a fallback icon.
– I tried to use multiple css background-image declarations like this:
style={{
backgroundImage: `url(./icons/fallback.svg), url(./icons/${item.extension}.svg)`,
}}
or like this:
style={{
backgroundImage: `url(./icons/fallback.svg)`,
backgroundImage: `url(./icons/${item.extension}.svg)`,
}}
But it doesn't work, the fallback icon is not being used (or in one case I am not able to reproduce, both icon are shown, which is also undesired).
I tried to fetch the file to determine if it does exist, but the node server (i use create-react-app) is configured in a way that returns 200 or 304 even if the file isn't actually present.
I tried to use a solution which creates an image and uses onload and onerror events as beeng suggested in this question, which actually works fine – i am currently using slightly modified implementation of image-exists npm module – but I wasn't able to figure out how to refactor this function to simply return a boolean. Using console.log() and callbacks works fine; returning a boolean results in undefined. I suppose it is due to an asynchronous behaviour od Image methods, but I wasn't able to create a workaround – maybe using a Promise API?
My code:
exists = src => {
const checks = {};
return callback => {
if (src in checks) {
return callback(checks[src]);
}
let img = new Image();
img.onload = function() {
checks[src] = true;
callback(true);
};
img.onerror = function() {
checks[src] = false;
callback(false);
};
img.src = src;
};
};
Render method:
render() {
// So far so good, logs as expected, but not such useful
console.log(this.exists('./icons/jpg.svg')(bool => {
if(bool) {
console.log('yes')
} else {
console.log('no');
}
}));
// ...
}
If I try to return a boolean directly, it results in undefined:
render() {
console.log(this.exists('./icons/jpg.svg')(bool => bool));
// ...
}
You are right, the function does not return a boolean because this is the parameter of the callback of your exists function, which is called asynchronously. The solution is to render your icon asynchronously too, something like...
this.exists(img)(bool => {
if (bool) {
render(img)
} else {
render('fallback.svg');
}
}
O.K. I finally promisify the whole thing. I hooked the former exists function (now checkImage) to a promise chain(saw… massacre…) which is triggered by reading files to upload and results in setState and rerender:
The url checking function:
checkImage = (path, fallback) => {
return new Promise(resolve => {
const img = new Image();
img.src = path;
img.onload = () => resolve(path);
img.onerror = () => resolve(fallback);
});
};
Calling with Promise.all():
// items are array of objects which contains file contents, name, extension etc...
checkIcons = items =>
Promise.all(
items.map(item => {
const url = `./icons/${item.extension}.svg`;
return this.checkImage(url, this.state.fallbackIconUrl).then(result => {
return { ...item, icon: result };
});
})
);
Definitely not the slickiest one in town and it would possibly need some caching (or may not – it does seem the browser can handle this by itself), but works fine.

Cannot access a key value pair of a changed object

Please excuse my code
From an external source , I am given the following external data which I name loxAP3
to which I am trying to firstly retrieve svg data related to the rooms.image property and then change the incoming svg data to work with react, using the following code.
createRoomData(loxAPP3, socket) {
console.log(loxAPP3)
let rooms = []
let rawRooms = loxAPP3.rooms
for (const rawRoom in rawRooms) {
rooms.push(rawRooms[rawRoom])
}
//add svg property with blank property value
rooms.forEach((room) => {
room.svg = ''
})
//fetch image data for each room in loxApp3.rooms
rooms.forEach((room) => {
const image = room.image
socket
.send(image)
.then(function(respons) {
//console.log("Successfully fetched svg image " + respons ); // success
room.svg = respons
//console.log(room.svg) // success console returns room.svg data
},
function(err) {
console.error(err);
}
);
})
this.setState({
rooms: rooms
}, () => {
console.log(rooms) // success rooms[0].svg is shown as having been populated
this.adjustSvGImageToReact()
})
}
console.log(rooms) // success rooms[0].svg is shown as having been populated
However the problem comes when I try and manipulate the room object, if I log a property that already existed from the original data, there is no problem, however if I try an fetch the .svg property it comes back not as undefined but as the empty string I first set it to be.
adjustSvGImageToReact() {
this.state.rooms.forEach((room)=>{
console.log(room.name) // success
console.log(room.uuid) // success
console.log(room.svg) //empty
})
}
Create an array of the socket.send() promises instead of calling them inside forEach
Then you can use Promise.all() to set the state and call adjustSvGImageToReact() after the socket requests have completed
const svgPromises = rooms.map((room) => {
const image = room.image
return socket
.send(image)
.then((respons)=> room.svg = respons)
})
Promise.all(svgPromises).then(() => {
this.setState({rooms: rooms}, () => {
console.log(rooms) // success rooms[0].svg is shown as having been populated
this.adjustSvGImageToReact()
});
}).catch(err=>console.log('One of the socket requests failed'))

Categories