React JS | Firebase | reCAPTCHA already rendered issue - javascript

I have a component like this -->
<form onSubmit={verifyOTP}>
....
.....
<div className="auth-btn-wrap">
<button disabled={isLoading ? true : false} type="submit" className="btn btrimary">
{isLoading ? (<CircularProgress />) : (<>Verify OTP</>)}</button>
{isLoading ? null : (<Link onClick={resendOTP}>Resend</Link>)}
</div>
<div id="recaptcha-container" />
</form>
verifyOTP function looks like this -->
const verifyOTP = (e: React.SyntheticEvent) => {
e.preventDefault();
if (window.confirmationResult) {
window.confirmationResult
.confirm(otp)
.then(async () => {
dispatch(signupStartStart({ user: { ...user, otp, otpConfirm: window.confirmationResult }, history }));
})
.catch((e: any) => {
console.error(e);
snack.error("Invalid OTP");
});
} else {
window.location.reload();
}
};
In my user saga file, action signupStartStart looks like this -->
{.......
.......
const result = yield call(sendOTPWithFb, {
phoneNumber: user.countryCode + user.phoneNumber, containerName: "recaptcha-container"
});
yield put (setLoading ({loading: false}));
if (result) {
yield put(showVerifyOTPSuccess());
snack.success('An OTP has been sent to you mobile');
} else {
snack.error("Unable to send OTP");
}
}
The function that sends OTP is this -->
export const sendOTPWithFb = async (data: any): Promise<boolean> => {
const { phoneNumber, containerName } = data
try {
const appVerifier = new FirebaseTypes.auth.RecaptchaVerifier(
containerName,
{
size: "invisible",
}
);
const confirmationResult = await firebase
.auth()
.signInWithPhoneNumber(`${phoneNumber}`, appVerifier);
window.confirmationResult = confirmationResult;
return true;
} catch (error) {
console.error(error);
return false;
}
};
Whenever I click on Resend OTP button, it gives me error that --->
"reCAPTCHA has already been rendered in this element"
PLease let me know how to resolve this issue

Related

React async call on infinite loading state in Chrome/Safari, works normal on Firefox

I have a modal that can create/update/delete data, with code being:
const [innerData, setInnerData] = useState(data);
const [dateString, setDateString] = useState(innerData.date.toISOString().slice(0, 10));
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [actionBarInProgress, setActionBarInProgress] = useState<string | null>(null);
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
action === "create"
? await onCreateAnnotation(innerData)
: await onUpdateAnnotation(innerData);
closeModal();
} catch (e) {
console.error(`Error updating annotation ${data.id}: ${e}`);
}
setActionBarInProgress(null);
};
const handleDeleteAnnotation = async () => {
try {
setDeleteLoading(true);
setActionBarInProgress("loading");
await onDeleteAnnotation(data.id);
setDeleteLoading(false);
setActionBarInProgress(null);
closeModal();
} catch (e) {
console.error(`Error updating annotation ${data.id}: ${e}`);
}
};
return (
<Modal shown={show} headerTitle={modalTitle} onClose={closeModal}>
<Form onSubmit={onSubmit}>
<Modal.Content>
...
</Modal.Content>
<Modal.Footer>
<div style={{ position: "relative" }}>
{action === "update" && (
<Button
onClick={handleDeleteAnnotation}
variant="destructive"
className={scss.annotationsDeleteButton}
loading={deleteLoading}
disabled={actionBarInProgress !== null}
>
Delete
</Button>
)}
<ActionBar
inProgressAction={actionBarInProgress}
primary={{
children: modalConfirmTitle,
type: "submit",
onClick: (actionId) => setActionBarInProgress(actionId),
}}
cancel={{
children: i18n.cancel,
onClick: () => closeModal(),
}}
/>
</div>
</Modal.Footer>
</Form>
</Modal>
);
I builted an example of how this could work, using a setTimeout function to simulate async calls (the following functions should be provided by the developer when using this component, that is why this are only examples to simulate async calls)
const sleep = (n: number) => {
return new Promise((resolve) => setTimeout(resolve, n));
};
fetch: async (): Promise<AnnotationItem[]> => {
await sleep(1000);
return [...API.data];
},
update: async (data: Omit<Partial<AnnotationItem>, "createdBy">): Promise<void> => {
// Simulate backend UPDATE call
console.log("update before");
await sleep(1000);
const annotationIndex = API.data.findIndex((annotation) => annotation.id === data.id);
if (annotationIndex === -1) return;
API.data[annotationIndex] = { ...API.data[annotationIndex], ...data };
console.log("update after");
},
create: async (data: Omit<AnnotationItem, "id" | "createdBy">): Promise<void> => {
// Simulate backend CREATE call
console.log("create before");
await sleep(1000);
const username = await API.getUser();
const newId = (API.data[API.data.length - 1].id as number) + 1;
const newAnnotation: AnnotationItem = { ...data, id: newId, createdBy: username };
API.data.push(newAnnotation);
console.log("create after");
},
delete: async (id: number | string): Promise<void> => {
// Simulate backend DELETE call
console.log("delete before");
await sleep(1000);
const annotationIndex = API.data.findIndex((annotation) => annotation.id === id);
if (annotationIndex === -1) return;
API.data.splice(annotationIndex, 1);
console.log("delete after");
},
when calling the component, we have this useEffect to fetch the initial data (which works fine always)
useEffect(() => {
let isDataFetchDone = true;
API.fetch().then((data) => isDataFetchDone && setAnnotationsData(data));
API.fetchAreasVisibleIn().then(
(areas) => isDataFetchDone && setAnnotationsAreasVisibleIn(areas)
);
return () => {
isDataFetchDone = false;
};
}, []);
and the functions, being passed as props:
onUpdateAnnotation={async (data) => {
await API.update(data);
setAnnotationsData(await API.fetch());
}}
onCreateAnnotation={async (data) => {
await API.create(data);
setAnnotationsData(await API.fetch());
}}
onDeleteAnnotation={async (id) => {
await API.delete(id);
setAnnotationsData(await API.fetch());
}}
on Mozilla Firefox it works perfectly, I can do all the CRUD and works fine.
But on Chrome and Safari, when I click at the buttons on the Modal, it never calls the functions (it never even shows the console.log that I left in the code to test)
The only error I got on chrome was this:
Unchecked runtime.lastError: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received

Why my try catch block won't execute ? - React

I have a register modal which has 3 step.
Fill up the info, getActivate Code and Success Message.
I Want when user filled to inputs and clicked the submit button if there is no error move to next Step
but if there is error then React Toastify will show the message.
My problem is why try catch block in MyComponent dosen't work ?
P.S: I'm using Formik and Yup
httpService
const handleExpectedError = (response: any) => {
if (response?.status >= 400 && response?.status < 500) {
const errors = response?.data?.errors;
const errPropertyName: string[] = Object.keys(errors);
toast.error(errors?.[errPropertyName?.[0]]?.[0]);
}
};
export const handleRegister = async (user: User): Promise<void> => {
try {
await axios.post(`${config.apiEndPoint}/auth/register`, user, header);
} catch ({ response }) {
handleExpectedError(response);
}
};
MyComponent
const [step, setStep] = useState(1);
const formik = useFormik({
initialValues: {
firstName: "",
lastName: "",
email: "",
phoneNumber: "",
password: "",
},
onSubmit: (value) => {
if (terms) {
handleNextStep(value);
}
},
validationSchema: registerSchema,
});
// Event Handler
// Value below is a referance to Formik object
const handleNextStep = async (value: any) => {
if (step === 1) {
try {
await handleRegister(value);
setStep(step + 1);
await handeGetActivateCode({ email: value.email });
} catch (error) {
setStep(1);
}
}
if (step !== 1) return setStep(step - 1);
};
In httpService file, you have used try-catch. In that catch you are trying to get the error in the curly braces, instead of doing like that if you do the following thing. then the catch block will work fine
export const handleRegister = async (user: User): Promise<void> => {
try {
await axios.post(`${config.apiEndPoint}/auth/register`, user, header);
} catch (response) {
handleExpectedError(response);
}
};

Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'hash')

I am trying to make a solution to store the file in IPFS , and then put the hash on the blockchain.
But when I try to upload the file to IPFS here comes the error message
Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'hash')
I am using react , ganache , node.js , ipfs-http-client
here's my app.js code
import React, {Component} from 'react'
import SimpleStorageContract from '../src/SimpleStorage.json'
import getWeb3 from './utils/getWeb3'
import './css/oswald.css'
import './css/open-sans.css'
import './css/pure-min.css'
import './App.css'
const ipfsAPI = require('ipfs-http-client');
const ipfs = ipfsAPI.create({host: 'localhost', port: '5001', protocol: 'http'});
const contract = require('truffle-contract')
const simpleStorage = contract(SimpleStorageContract)
let account;
// Declaring this for later so we can chain functions on SimpleStorage.
let contractInstance;
let saveImageOnIpfs = (reader) => {
return new Promise(function(resolve, reject) {
const buffer = Buffer.from(reader.result);
ipfs.add(buffer).then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
blockChainHash: null,
web3: null,
address: null,
imgHash: null,
isWriteSuccess: false
}
}
componentWillMount() {
ipfs.swarm.peers(function(err, res) {
if (err) {
console.error(err);
} else {
// var numPeers = res.Peers === null ? 0 : res.Peers.length;
// console.log("IPFS - connected to " + numPeers + " peers");
console.log(res);
}
});
getWeb3.then(results => {
this.setState({web3: results.web3})
// Instantiate contract once web3 provided.
this.instantiateContract()
}).catch(() => {
console.log('Error finding web3.')
})
}
instantiateContract = () => {
simpleStorage.setProvider(this.state.web3.currentProvider);
this.state.web3.eth.getAccounts((error, accounts) => {
account = accounts[0];
simpleStorage.at('0xe7D98C99d71438A072B020138dD75347792FA214').then((contract) => {
console.log(contract.address);
contractInstance = contract;
this.setState({address: contractInstance.address});
return;
});
})
}
render() {
return (<div className="App">
{
this.state.address
? <h1>CONNECT THE CONTRACT ADDRESS:{this.state.address}</h1>
: <div/>
}
<h2>UPLOAD TO IPFS:</h2>
<div>
<label id="file">CLICK TO UPLOAD THE FILE</label>
<input type="file" ref="file" id="file" name="file" multiple="multiple"/>
</div>
<div>
<button onClick={() => {
var file = this.refs.file.files[0];
var reader = new FileReader();
// reader.readAsDataURL(file);
reader.readAsArrayBuffer(file)
reader.onloadend = function(e) {
console.log(reader);
saveImageOnIpfs(reader).then((hash) => {
console.log(hash);
this.setState({imgHash: hash})
});
}.bind(this);
}}>UPLOAD TO IPFS AND RETURN THE HASH</button>
</div>
{
this.state.imgHash
? <div>
<h2>imgHash:{this.state.imgHash}</h2>
<button onClick={() => {
contractInstance.set(this.state.imgHash, {from: account}).then(() => {
console.log('HASH HAS BEEN WRITE ON BLOCKCHAIN');
this.setState({isWriteSuccess: true});
})
}}>PUT HASH ON BLOCKCHAIN:contractInstance.set(imgHash)</button>
</div>
: <div/>
}
{
this.state.isWriteSuccess
? <div>
<h1>HASH IS ON THE BLOCK CHAIN</h1>
<button onClick={() => {
contractInstance.get({from: account}).then((data) => {
console.log(data);
this.setState({blockChainHash: data});
})
}}>READ HASH ON BLOCKCHAIN:contractInstance.get()</button>
</div>
: <div/>
}
{
this.state.blockChainHash
? <div>
<h3>READ THE HASH ON BLOCKCHAIN:{this.state.blockChainHash}</h3>
</div>
: <div/>
}
{
this.state.blockChainHash
? <div>
<h2>BROWSER ACCESS:{"http://localhost:8080/ipfs/" + this.state.imgHash}</h2>
<img alt="" style={{
width: 1600
}} src={"http://localhost:8080/ipfs/" + this.state.imgHash}/>
</div>
: <img alt=""/>
}
</div>);
}
}
export default App
i hope someone can be my savior , thank you really much.
It looks like you're assuming that the response will always be defined. Please check response[0] to see if the response is valid. It looks like onloadend is called asynchronously. Try onload to ensure you're passing information synchronously.
let saveImageOnIpfs = (reader) => {
return new Promise(function(resolve, reject) {
const buffer = Buffer.from(reader.result);
ipfs.add(buffer).then((response) => {
console.log(response)
if(response[0] !== undefined && response[0].hash !== undefined){
resolve(response[0].hash);
}else{
console.log(response)
}
}).catch((err) => {
console.error(err)
reject(err);
})
})
}
At last , my answer has been solved
let saveImageOnIpfs= (reader) => {
return new Promise(async(resolve, reject) => {
try {
const buffer = Buffer.from(reader.result);
let results = await ipfs.add(buffer);
let hash1 = results.path;
resolve(hash1);
} catch (err) {
console.error(err);
reject(err);
}
})
}
thank you for your answer to help me.

How to redirect a user based on Firebase Authentication status?

I would like to redirect users when they sign in with Github or others based on whether they are a new user or a returning user. I'm having trouble accessing the isNewUser property referenced in this answer: How to differentiate signin and signup user in firebase using google auth?
I have a standard sign in function:
const signinWithGoogle = () => {
return auth.signInWithPopup(googleProvider)
.then((response) => {
handleUser(response.user)
})
}
This is the handleUser function:
const handleUser = (rawUser) => {
if (rawUser) {
const currentUser = formatUser(rawUser)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
if (currentUser.providerData[0].isNewUser===true) {
history.push("/onboarding")
} else {
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
And this is formatUser:
const formatUser = (user) => {
return {
uid: user.uid,
email: user.email,
name: user.displayName,
provider: user.providerData[0].providerId,
avatar: user.photoURL,
}
}
Can anyone see what I'm doing wrong, please?
Cheers, Matt
EDIT:
If we pass the response to the HandleUser function and console log response.additionalUserInfo.isNewUser we get 'true'. However, if we use that in our if statement, it seems to be ignored for some reason
const handleUser = (response) => {
if (response) {
console.log("response: ", response.additionalUserInfo.isNewUser)
const currentUser = formatUser(response.user)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
console.log('response', response)
console.log('additional info', response.additionalUserInfo)
const isNew = response.additionalUserInfo.isNewUser
console.log('isNewUser', isNewUser)
if (isNew) {
console.log('redirecting to /onboarding')
history.push("/onboarding")
} else {
console.log('redirecting to /')
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
EDIT 2: Here is the output from the console logs
That error is coming from the signInWithGithub function in the modal
async function signInGitHub() {
try {
await signinWithGitHub()
}
catch(err) {
console.log("Error: ",err.code)
}
finally {
closeModal();
}
}
It looks like you are passing a User to that function and not the raw response. The isNewUser is present on the additionalUserInfo property. Please try refactoring as shown below:
const handleUser = (rawUser) => {
if (rawUser) {
const currentUser = formatUser(rawUser.user)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
if (currentUser.additionalUserInfo.isNewUser) {
history.push("/onboarding")
} else {
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
Also make sure you pass the raw response:
handleUser(response.user)

how to update(F5) properly after a post. to display a get

I would like to explain my problem of the day.
currently, I post a message in my database, then in the same component I perform a get to display the data sent just before.
my problem and the next one I have to update the page manually to display my get,
I would like to find a solution to directly display the result without having to press F5
How can I fix this issue?thx all
make room for the code :)
post
postBack(e,) {
e.preventDefault();
const payload = {
phone: this.state.phone,
total: this.state.total ,
}
axios.post('******', payload)
.then(res => {
if (res.error) {
alert(res.error);
}
}).catch(e => {
console.error(e);
}).finally(() => this.setState({
redirect: true
}));
}
get
getRandom = async () => {
const res = await axios.get(
'*****'
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render
render() {
let datas = this.state.data.map(chat => {
return (
<form onSubmit={(e) => this.handleSubmit(e, chat,)}>
<label>
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Envoyer</button>
</form>
<div key={chat.id}>
<div key={chat.id}>
<p> your name is {chat.name} </p>
</div>
</div>
)
})
return (
<div>
{datas}
</div>
)
}
}
You can update data state after POST request is success.
The code seems incomplete but it would be something like this
postBack(e,) {
e.preventDefault();
const payload = {
phone: this.state.phone,
total: this.state.total ,
}
axios.post('******', payload)
.then(res => {
if (res.error) {
alert(res.error);
} else { // here should be place to update state
this.setState({ data: payload })
}
}).catch(e => {
console.error(e);
}).finally(() => this.setState({
redirect: true
}));
}

Categories

Resources