How to send multipart form data with React (Next) / Redux / Saga? - reactjs

I am sending img file with usual React Saga and Next.js. I never worked with sending multipart form data so I don't know how to do it. I usually send image in a body in base64 format.
This is UI part:
const onUploadLogo = (e) => {
var img = e.target.files[0]
let formData = new FormData()
formData.append("file", img)
console.log('image check => ', img)
console.log('formData check => ', formData)
updateLogoFunc({ // this is the API call
formData // body
})
}
<label className="custom-file-upload">
<input type="file" onChange={onUploadLogo} accept="image/*" />
Attach
</label>
In 'image check => ' I see that my image is uploaded, I see the file, however 'formData check => ' gives me undefined. Just fyi, saga call of API works just fine, no problem with that. If you need the code of saga:
export function* updateLogoSaga(action) {
try {
yield put(setLoader(true))
const response = yield ApiStore.garage.post(`UpdateLogo`, action.values)
yield put(setLoader(false))
} catch (error) {
yield put(setLoader(false))
}
}
My problem is I do not know how to send this damn image as multipart form data by POST call.

var img = e.target.files[0]
const formData = new FormData();
formData.append("file", img);
So far you got the file, but it is important how you are going to send it to the server.
If you need to send a buffer
const buffer = await file.arrayBuffer();
or as bytes
// each entry of array should contain 8 bits
const bytes = new Uint8Array(buffer);
So now your data is ready, you have to send it to the server. But it is not clear how you are going to send it to the server. are you going to send the image separete from other form inputs (In some case you might verify the image so you handle it separately). If you are going to send the image with form, together with other inputs inside a submitHandler it is better you write the above code inside onChange, set the file as state value and inside submitHandler you dispatch the input values. So far is the process of handling the image.
In saga, you have to create an action with type and your saga store would be listening to that type and once it receives that type, it will call the updateLogoSaga and your updateLogoSaga is seems correct.

Related

Performances issues while decrypting data on my next.js app

I've been struggling with my app last days.
while communicating with the API all data (shown or not by the app) could be seen throught the requests console, so i decided to encrypt the data send by the API, and decrypt the encrypted data in the app directly, so the request doesn't have the "clear version" displayed.
apparently no problems to encrypt, but even tho decrypting works, it freezes the app for a decent time each time new data arrives, i've been tryng async, but it doesn't change anything.
(the data is an array filled with objects)
Here is the code of the update function :
const fetchData = (setter) => {
const encrypter = new Cryptr(process.env.NEXT_PUBLIC_ENCRYPT_KEY);
fetch(process.env.NEXT_PUBLIC_DATA_FETCHING_URL)
.then((response) => response.text())
.then((data) => {
const decryptData = new Promise((resolve, reject) => {
try {
console.info('Decrypting...');
let decryptedData = encrypter.decrypt(data);
console.info('Parsing...');
let parsedData = JSON.parse(decryptedData);
console.info('Data parsed !');
resolve(parsedData);
} catch {
reject('ERROR DURING DATA DECRYPTION');
}
})
.then((parsedData) => setter(parsedData))
.catch((err) => console.error(err));
});
};
As you can see, i'm usingthe cryptr module (6.0.3), the app is running on next.js (12.2.5).
I tested many things, including the freeze provenance, but it seems like it's the decryption (not the parse or the setter())
PS: The js setter() function is the setter of the state that contains data from the server.
Is there a way to handle those unwanted freezes ?
Thanks for helping me with this 😊

Uploading multiple content to Cloudinary React js

I'm building this website where users can sometimes upload one audio or one image and sometimes both at the same time to cloudinary. I'm able to upload from the front-end (react.js) one or the other (image or audio) but not both at the same time.
I saw this post that says that it is not possible except if we "write a script and use multi-threads (up to 10 threads) to upload many files at once. " I have no idea what it means in the context of react.js | JavaScript.
My code is the following:
I first call the handleUploadCloudinary with its parameter. The function is being called once the data is ready to be published.
const publishTheEntry = () => {
const {
mainText,
mediaAudio,
mediaImg
} = formData;
if(mediaAudio !== ""){
handleUploadCloudinary("audio");
};
if(mediaImg !== ""){
handleUploadCloudinary("image");
};
};
The handleUploadCloudinary() is the following:
const handleUploadCloudinary = (mediaType) => {
const {
mediaAudio,
mediaImg
} = formData;
const formDataCloudinary = new FormData();
formDataCloudinary.append("upload_preset", my_var_here);
formDataCloudinary.append("timestamp", (Date.now() / 1000) | 0);
if(mediaType === "img"){
formDataCloudinary.append("file", mediaImg);
axios.post(
"https://api.cloudinary.com/v1_1/sismographie-cloud/image/upload",
formDataCloudinary
).then((response) => {
console.log(response);
let url = response.data.secure_url;
setFormData({...formData, mediaImg: url});
})
}else if(mediaType === "audio"){
formDataCloudinary.append("file", mediaAudio);
axios.post(
"https://api.cloudinary.com/v1_1/sismographie-cloud/upload",
formDataCloudinary
).then((response) => {
let url = response.data.secure_url;
setFormData({...formData, mediaAudio: url});
})
}
};
Even if, for when both audio + image are stored into the state, I can console.log() both of the conditions, the state won't bot update the formData with both. One will successfully sate the state with a new cloudinary link while the other one will remain a buffer.
You can loop through your resources list and upload assets one by one, or create new threads at the backend (best practice).
This link is a demo for uploading multiple files using Axios and React:
https://codesandbox.io/s/chunked-client-side-upload-forked-vqx6mp?file=/src/CldCustUploadLgRestApi.js

Axios post request with formData

I want to make a form with multiple input (two text and one image).
Furthermore, I'm using react, axios, formik and I submit my data to strapi.
With only one text and one file, it works fine, but when I try to add a new append, I've got the error
When using multipart/form-data you need to provide your data in a JSON 'data' field.
const onSubmit = async (event) => {
const data = new FormData()
//This works
data.append('data', JSON.stringify({title:event.title}))
data.append('files.cover', file)
//This doesnt
data.append('title', JSON.stringify({title:event.title}))
data.append('description', JSON.stringify({description:event.description}))
data.append('files.cover', file)
try{
const response = await axios.post('http://localhost:1337/foodtrucks', data,
{
headers: userToken && { Authorization: `Bearer ${userToken}` }
},
)
// const data = await response.json()
console.log("response", response)
} catch(err){
// setError(err.response.data.message[0].messages[0].message)
setError("error")
console.log(err)
}
}
I tried to add headers, to use form-data, etc. Nothing works.
I think axios is meant for calling APIs - it may not be easy (or possible to send form data).
If you're running in the Browser, you may want to use plain XMLHttpRequest. I came across this article, which defines the submitForm function. It has helped me a lot on many projects.

FileReader - how to update local state after store is updated?

I'm playing around with a food recognition api.
I have a component with a local state called ingredients.
In the component, I have an input tag that accepts a file image upload and calls cameraHandler method onChange. The method uses FileReader to convert the image into Base64
Once the FileReader is finished encoding the image, the method calls a redux action fetchIngredientsFromImage to post the base64 image into a route to trigger to trigger an API call (to analyze the ingredients in the image).
The response is sent back to the front end, and used to update store.
So basically, the API call is successful, I get the data I need, and store is updated successfully. Great.
But what I also need to do, is update my local ingredients state. But I don't know how to wait for store to be updated before calling setState.
I've tried componentDidUpdate with if(this.props !== prevProps) methodToUpdateLocalState(), but this doesn't work because for some reason the component won't re-render after store is updated.. Turns out that everything inside componentDidUpdate runs first, and store is updated afterwards. I feel like also isn't necessary (probably).
I also tried .then the awaited readers inside cameraHandler, but .then is undefined.
I'd appreciate any input I could get. Really at a loss here, because I have the data, and I just need to somehow grab it so I can setState.
Component
class RecipesSearch extends Component {
state = {
ingredients: [], //need to update this after store is updated, but how?
};
cameraHandler = async (event) => {
const { fetchIngredientsFromImage } = this.props;
const file = event.target.files[0];
const reader = new FileReader();
await reader.readAsDataURL(file);
reader.onloadend = async () => {
const imgBase = reader.result.replace(/^data:image\/(.*);base64,/, '');
await fetchIngredientsFromImage(imgBase); //.then here is undefined
};
};
render(){
<input
className="form-check-input"
type="file"
name="camera"
accept="image/*"
onChange={this.cameraHandler}
/>
}
Actions
const fetchIngredientsFromImage = (imgBase) => async (dispatch) => {
const { data } = await axios.post(`/api/camera/`, { imgBase });
return dispatch(setIngredientsFromCamera(data)); //successfully updates store
};
as a workaround I made an axios.post call inside cameraHandler. Not proud of it, because I'd like to utilize store and keep it consistent with my other methods, but for the time being it'll do I guess.
cameraHandler = async (event) => {
// const { loadIngredientsFromImage } = this.props;
const file = event.target.files[0];
const reader = new FileReader();
await reader.readAsDataURL(file);
reader.onloadend = async () => {
const imgBase = reader.result.replace(/^data:image\/(.*);base64,/, '');
await axios
.post(`/api/camera/`, { imgBase })
.then((response) => this.setState({ ingredients: response.data }));
};
};

Value of state variable is lost - React

I want to build a CRUD in React with Laravel and Firebase. Everything is perfect when I'm working with text, but I got trouble when I try to upload an image to Firebase Storage. I can save it but I can't get its URL.
I wrote 2 "console.log". In the first one the URL is there, but the second one (when I try to get the URL from the state variable) doesn't return anything.
handleSubmit = event =>{
event.preventDefault();
const {imagen} = this.state;
if(imagen!=null){
const uploadTask = storage.ref(`imagenes/${imagen.name}`).put(imagen);
uploadTask.on('state_changed',
(snapshot) => {
const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
this.setState({progress});
},
(error) => {
console.log(error);
},
() => {
storage.ref('imagenes').child(imagen.name).getDownloadURL().then(url => {
this.setState({url});
console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)
})
});
}
var direccion = null;
const form = event.target;
let data = new FormData(form);
data.append('url', this.state.url);
console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE)
If you want to check the entire file:
https://github.com/AndresVasquezPUCE/project/blob/master/pelicula
I'm not a professional, so please don't be rude :D
this.setState is asynchronous
If you want to get the updated state value, add a callback and access the new state there like
this.setState({ url: 'some url'}, () => {
conosle.log(this.state.url);
});
Data is loaded from Firebase asynchronously. By the time your console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE) the data hasn't been loaded from Firebase yet, and the then hasn't been called yet.
Any code that needs the data from Firebase needs to either be inside the then() callback (such as console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)) or be called from there (such as this.setState({url})).

Resources