How to download a kind of stream file in React js? - reactjs

I would like to download a stream file in my project.
Indeed the response form the API is like a blob.
Here i leave my code;
id and filterBody arrives as props to my component.
Really i confused if some where there are mistakes or No?
Can any one help me to find my mistakes?
Thank you.
const fetchBlobFile = async (setBlobFile, id, filterBody) => {
try {
let response = await get(`http://localhost:4000/error/table/export?error_id=${id}`, filterBody)
setBlobFile(response?.data?.blob())
} catch (error) {
}
}
function exportXslFile ({id, filterBody}) {
const [blobFile, setBlobFile] = useState(null)
useEffect(() => {
fetchBlobFile(setBlobFile, id, filterBody)
}, [id])
const export = () => {
const url = window.URL.createObjectURL(blobFile);
window.location = url
}
return (
<button className="export" onClick={export}><ExportXslFile /> Export</button>
)
}

I solved the problem as following.
This code completely works; but only be careful to import necessary packages.
const fetchBlobFile = async (setBlobFileLoading, id, body) => {
try {
const a = document.createElement("a");
a.style.display = "none";
document.body.appendChild(a);
setBlobFileLoading(true);
var data = JSON.stringify(body);
var config = {
method: 'post',
url: `http://localhost:4000/table/export?errorCsv_id=${id}`,
headers: {
'Content-Type': 'application/json'
},
data: data
};
let response = await axios(config);
setBlobFileLoading(false)
const blobFile = new Blob([response?.data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blobFile);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
} catch (error) {
}
}
function exportXslFile ({id, body}) {
const [blobFileLoading, setBlobFileLoading] = useState(false)
const export = () => {
fetchBlobFile(setBlobFileLoading, id, filterBody)
}
return (
{blobFileLoading && <div className="loading fixed"><Loader /></div>} // set up loader
<button className="export" onClick={export}>Export</button>
) //download button
}

Related

Adding multiple API calls to a map in reactjs using axios

I need my API call to pull NFT data from moralis and add it to a map so it can later be rendered.
This all works fine, however the limit per call on moralis is 100 lines. I have added a second API call using cursor pagination. Both API calls work individually but when I try to add both to the map it just renders the most recent one. Is there a way to show everything in the collection? Thanks in advance!!
Here is the code I currently have to call the API:
async function callApi() {
var provider = await web3Modal.connect();
web3 = new Web3(provider);
await provider.send('eth_requestAccounts');
var accounts = await web3.eth.getAccounts();
account = accounts[0];
vaultcontract = new web3.eth.Contract(VAULTABI, STAKINGCONTRACT);
let config = { 'X-API-Key': moralisapikey, 'accept': 'application/json', cursor: '' };
const nfts0 = await axios.get((moralisapi + `nft/${NFTCONTRACT}/owners?chain=polygon&format=decimal&limit=100`), { headers: config })
.then(output => {
const { result } = output.data
return result;
})
const nfts1 = await axios.get((moralisapi + `nft/${NFTCONTRACT}/owners?chain=polygon&format=decimal&limit=100`), { headers: config })
.then(output => {
const { result } = output.data
return result;
})
const nfts = (nfts0, nfts1)
const apicall = await Promise.all(nfts.map(async i => {
let item = {
tokenId: i.token_id,
holder: i.owner_of,
wallet: account,
}
return item
}))
const stakednfts = await vaultcontract.methods.tokensOfOwner(account).call()
.then(id => {
return id;
})
const nftstk = await Promise.all(stakednfts.map(async i => {
let stkid = {
tokenId: i,
}
return stkid
}))
getNfts(apicall)
getStk(nftstk)
console.log(apicall);
setLoadingState('loaded')
}

How to correctly write lambda expression in fetch

I have simple fetch in my react.js project:
export function getUserById(id) {
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json','Access-Control-Allow-Origin':'http://localhost:9000' },
mode: 'no-cors',
};
var res = fetch("http://localhost:9000/user/"+id);//there should be a lot of strange lambdas..
return res;
}
I use this function in other file:
var user = getUserById(userStr);
//some code
//more code
var email=user.mail;
var id = user.id;
Unfortunately - res is always Promise. I don't feel functional programming and react but I need to do it. I know that there should be some lambda expression, but I have no idea how to write them. As you see I need to take email and id from returning JSON.
Thanks in advance.
You need to await the fetch call.
See this example on MDN.
export async function getUserById(id) {
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json','Access-Control-Allow-Origin':'http://localhost:9000' },
mode: 'no-cors',
};
var res = fetch("http://localhost:9000/user/"+id);//there should be a lot of strange lambdas..
return res;
}
var user = await getUserById(userStr);
//some code
//more code
var email=user.mail;
var id = user.id;
Edit: You don’t need to await the fetch call, you only need to await getUserById(userStr).
Answer is not that easy. Proper way (await returns promise!) is little bit more problematic. There is my solve ;) :
return fetch("http://your.fancy.link").then((response) => response.json())
and
function App() {
const [state, setState] = useContext(MyContext);
const [loading,setLoading] = useState(true);
const readUrlParamsFromAuthentication = () => {
var url_string = window.location.href; //window.location.href
var url = new URL(url_string);
var userStr = url.searchParams.get("user-id");
var userId = null;
var userEmail = null;
if (userStr) {
getUserById(userStr).then(user=>
{
localStorage.setItem('email', user.email);
setState({ email: user.email, isLogged: true });
console.log("TUTAAAAJ");
console.log(user.email,user.id,state.isLoggedIn);
setLoading(false);
}
);
console.log("UserStr");
}
}
useEffect(() => {
readUrlParamsFromAuthentication();
}, [])
there should be your return html ;)

Can't upload multiple files by using Multer on React

Multer worked fine when I upload multiple images by using Postman but when I try to send files as an array from the front-end, it always return files is undefined and files is not iterable.
I think the problem is in the front-end React. How to fix this problem?
Back-end
router.post('/multer',auth ,upload.array('image'), async (req, res) => { //NOTE image is the field name
try {
const urls = []
const files = req.files;
console.log('Upload cloudinary running '+ files)
.
.
.
Front-end React
import React, {Fragment, useState, useEffect} from 'react';
const Dashboard = ({auth: { user, loading }}) => {
.
.
.
const [files, setFiles] = useState([]);
const handleChange = e => {
const file_reader = new FileReader();
const file = e.target.files[0];
let file = e.target.files[0];
file_reader.onload = () => {
setFiles([...files, { image: file_reader.result }]);
};
file_reader.readAsDataURL(file);
}
const handleSubbmitFile = (e) => {
e.preventDefault();
if(!files) return;
uploadImage(files);
}
const uploadImage = async (base64EncodedImage) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
const body = JSON.stringify({files: base64EncodedImage});
await axios.post('/api/blogs/multer', body, config);
} catch (error) {
console.log(error);
}
}
return(
<form onSubmit={handleSubbmitFile} className="form-outline">
<input name="image" onChange={handleChange} type="file"
class="form-control-file" id="exampleFormControlFile1"
accept="image/*"/>
</form>
)
}
I found the problems and I fixed it by using formData in handleSubbmitFile and set the property filed to image by using append.
const handleSubbmitFile = (e) => {
e.preventDefault(); //NOTE prevent from reload the page
let formData = new FormData();
for(var i = 0 ; i < files.length; i++){
formData.append('image',files[i])
}
if(!files) return;
uploadImage(formData);
}
Another problems is that using Json.stringify() before using axios.
I didn't use Json.stringify() to convert formData before sending it to back-end via axios. As a result, Multer work well without problems
const uploadImage = async (formData) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
await axios.post('/api/blogs/multer', formData, config);
} catch (error) {
console.log(error);
}
}

501 Not Implemented error in small web app

I am trying to upload images to an S3 bucket with react and expressjs. When I attempt to upload the image I get a 501 Not Implemented Error. I am using axios to contact the end point I created in the server code.
My react code:
class FileUpload extends Component {
state = {
file: null
};
submitFile = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', this.state.file[0]);
axios.post(`test-upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
// handle your response;
}).catch(error => {
// handle your error
});
}
handleFileUpload = (event) => {
this.setState({file: event.target.files});
}
render () {
return (
<form onSubmit={this.submitFile}>
<input label='upload file' type='file' onChange=
{this.handleFileUpload} />
<button type='submit'>Send</button>
</form>
);
}
}
export default FileUpload;
My server code:
const uploadFile = (buffer, name, type) => {
const params = {
ACL: 'public-read',
Body: buffer,
Bucket: process.env.S3_BUCKET,
ContentType: type.mime,
Key: `${name}.${type.ext}`
};
return s3.upload(params).promise();
};
app.use('/', (req,res) =>{
res.send(JSON.stringify({ greeting: `Hello!` }));
});
// Define POST route
app.post('/test-upload', (request, response) => {
const form = new multiparty.Form();
form.parse(request, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const timestamp = Date.now().toString();
const fileName = `bucketFolder/${timestamp}-lg`;
const data = await uploadFile(buffer, fileName, type);
return response.status(200).send(data);
} catch (error) {
return response.status(400).send(error);
}
});
});
This was done by following a guide on the internet but there seems to be something missing that I just can't figure out.
Figured it out in the end,
axios.post(/test-upload,
should have been
axios.post(http://localhost:3000/test-upload,

React and reCAPTCHA v3

Is there any easy way to use reCAPTCHA v3 in react? Did a google search an can only find components for v2. And only react-recaptcha-v3 for v3.
But I get an error Invalid site key or not loaded in api.js when I try to use the component.
Hey you don't need a package, its just an unnecessary package you don't need.
https://medium.com/#alexjamesdunlop/unnecessary-packages-b3623219d86
I wrote an article about why you shouldn't use it and another package.
Don't rely on some package! Rely on google instead :)
const handleLoaded = _ => {
window.grecaptcha.ready(_ => {
window.grecaptcha
.execute("_reCAPTCHA_site_key_", { action: "homepage" })
.then(token => {
// ...
})
})
}
useEffect(() => {
// Add reCaptcha
const script = document.createElement("script")
script.src = "https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"
script.addEventListener("load", handleLoaded)
document.body.appendChild(script)
}, [])
return (
<div
className="g-recaptcha"
data-sitekey="_reCAPTCHA_site_key_"
data-size="invisible"
></div>
)
I am teaching myself React + TypeScript and this is what I came up with to implement recaptcha v3.
I wanted a simple solution that would allow me to:
get the token dynamically only when the form is submitted to avoid timeouts and duplicate token errors
use recaptcha only on some components for privacy reasons (eg. login, register, forgot-password) instead of globally defining recaptcha api.js in index.html
require the least code possible to implement in a component
reCAPTCHA.ts
declare global {
interface Window {
grecaptcha: any;
}
}
export default class reCAPTCHA {
siteKey: string;
action: string;
constructor(siteKey: string, action: string) {
loadReCaptcha(siteKey);
this.siteKey = siteKey;
this.action = action;
}
async getToken(): Promise<string> {
let token = "";
await window.grecaptcha.execute(this.siteKey, {action: this.action})
.then((res: string) => {
token = res;
})
return token;
}
}
const loadReCaptcha = (siteKey: string) => {
const script = document.createElement('script')
script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${siteKey}`
document.body.appendChild(script)
}
To use this class declare it as a property in the component:
recaptcha = new reCAPTCHA((process.env.REACT_APP_RECAPTCHA_SITE_KEY!), "login");
And on form submit get the token that you need to pass to backend:
let token: string = await this.recaptcha.getToken();
To verify the token on the backend:
recaptcha.ts
const fetch = require("node-fetch");
const threshold = 0.6;
export async function validateRecaptcha(recaptchaToken: string, expectedAction: string) : Promise<boolean> {
const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
const url = `https://www.recaptcha.net/recaptcha/api/siteverify?secret=${recaptchaSecret}&response=${recaptchaToken}`;
let valid = false;
await fetch(url, {method: 'post'})
.then((response: { json: () => any; }) => response.json())
.then((data: any)=> {
valid = (data.success && data.score && data.action && data.score >= threshold && data.action === expectedAction);
});
return valid;
}
I have very limited experience with JS/TS and React but this solution does work for me. I welcome any input on improving this code.
You can use react-google-recaptcha3 npm package (size: ~5 KB)
npm i react-google-recaptcha3
Usage
import ReactRecaptcha3 from 'react-google-recaptcha3';
const YOUR_SITE_KEY = '';
function App() {
// load google recaptcha3 script
useEffect(() => {
ReactRecaptcha3.init(YOUR_SITE_KEY).then(
(status) => {
console.log(status);
}
);
}, [])
}
Now on form submit you need to generate token and then append it to your form data
const submit = () => {
const formData = { name: "John", lastname: "Doe" }
ReactRecaptcha3.getToken().then(
(token) => {
console.log(token);
formData.token = token;
// send request to backend
fetch(url, { method: 'POST', body: JSON.stringify(formData) }).then(...)
},
(error) => {
console.log(error);
}
);
};
Now in backend you need to validate token
const request = require('request-promise');
const secretKey = YOUR_RECAPTCHA_SECRET_KEY;
const userIp = 'USER_IP';
request.get({
url: `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${recaptchaToken}&remoteip=${userIp}`,
}).then((response) => {
// If response false return error message
if (response.success === false) {
return res.json({
success: false,
error: 'Recaptcha token validation failed'
});
}
// otherwise continue handling/saving form data
next();
})
Stackblitz example
Try this one!
https://github.com/t49tran/react-google-recaptcha-v3
npm install react-google-recaptcha-v3
You can also create your own custom hook useReCaptcha with React (Typescript):
// hooks/useReCaptcha.ts
import { RECAPTCHA_KEY, RECAPTCHA_TOKEN } from 'config/config'
import { useEffect, useState } from 'react'
const showBadge = () => {
if (!window.grecaptcha) return
window.grecaptcha.ready(() => {
const badge = document.getElementsByClassName('grecaptcha-badge')[0] as HTMLElement
if (!badge) return
badge.style.display = 'block'
badge.style.zIndex = '1'
})
}
const hideBadge = () => {
if (!window.grecaptcha) return
window.grecaptcha.ready(() => {
const badge = document.getElementsByClassName('grecaptcha-badge')[0] as HTMLElement
if (!badge) return
badge.style.display = 'none'
})
}
const useReCaptcha = (): { reCaptchaLoaded: boolean; generateReCaptchaToken: (action: string) => Promise<string> } => {
const [reCaptchaLoaded, setReCaptchaLoaded] = useState(false)
// Load ReCaptcha script
useEffect(() => {
if (typeof window === 'undefined' || reCaptchaLoaded) return
if (window.grecaptcha) {
showBadge()
setReCaptchaLoaded(true)
return
}
const script = document.createElement('script')
script.async = true
script.src = `https://www.google.com/recaptcha/api.js?render=${RECAPTCHA_KEY}`
script.addEventListener('load', () => {
setReCaptchaLoaded(true)
showBadge()
})
document.body.appendChild(script)
}, [reCaptchaLoaded])
// Hide badge when unmount
useEffect(() => hideBadge, [])
// Get token
const generateReCaptchaToken = (action: string): Promise<string> => {
return new Promise((resolve, reject) => {
if (!reCaptchaLoaded) return reject(new Error('ReCaptcha not loaded'))
if (typeof window === 'undefined' || !window.grecaptcha) {
setReCaptchaLoaded(false)
return reject(new Error('ReCaptcha not loaded'))
}
window.grecaptcha.ready(() => {
window.grecaptcha.execute(RECAPTCHA_KEY, { action }).then((token: string) => {
localStorage.setItem(RECAPTCHA_TOKEN, token)
resolve(token)
})
})
})
}
return { reCaptchaLoaded, generateReCaptchaToken }
}
export default useReCaptcha
Then in the login component for example, you can call this custom hook:
// Login.ts
import React from 'react'
import useReCaptcha from 'hooks/useReCaptcha'
const LoginPageEmail = () => {
const { reCaptchaLoaded, generateReCaptchaToken } = useReCaptcha()
const login = async () => {
await generateReCaptchaToken('login') // this will create a new token in the localStorage
await callBackendToLogin() // get the token from the localStorage and pass this token to the backend (in the cookies or headers or parameter..)
}
return (
<button disabled={!reCaptchaLoaded} onClick={login}>
Login
</button>
)
}
export default LoginPageEmail

Resources