Handling UI when a transaction is in pending (MetaMask) - reactjs

I have a Metamask payment that works fine. It's triggered by an onClick of a button. I want to show something to the user during the pending of the transaction, but I can't figure out how since the promise returned is already the mined transaction. This is the code:
web3js.eth.sendTransaction({
to: '0x6Dc8956E655Ccd80187265107b848D8c5B6d2459',
from: address,
})
.then(function (txHash) {
console.log(txHash);
}).catch(error => {
console.log(error);
});
})

you need to use the state of your component
In constructor:
this.state = {willShowLoader: false}
In your onclick function (the second parameter is the callback)
this.state.setState({willShowLoader: true}, sendTransaction)
In your send transaction function: (note the setState inside of then)
web3js.eth.sendTransaction({
to: '0x6Dc8956E655Ccd80187265107b848D8c5B6d2459',
from: address,
})
.then(function (txHash) {
this.setState({willShowLoader:false})
console.log(txHash);
}).catch(error => {
console.log(error);
});
})
Then in your render method: (using conditional rendering)
(this.state.willShowLoader)?<Spinner/>:<notSpinner/>

Related

Cann't set the state data from response data

I'm doing in the axios to get data from webAPI . The problem is state is empty
componentDidMount() {
uploadFilesService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
console.log('check 1', response.data)
console.log('check 2', this.state.fileInfos)
});
}
The other project with the same method is OK .
You can the use second argument of setState
this.setState({
fileInfos: response.data,
}, () => {
// do something with the new state
});
From React doc:
The second parameter to setState() is an optional callback function
that will be executed once setState is completed and the component is
re-rendered. Generally we recommend using componentDidUpdate() for
such logic instead.

Send Rest call in a loop

I have a Rest API that in order to get the end data AKA:ENDDATA I need to send few requests (depends how deep). So when sending the first request the return response will have in the body the nextUri and so on…
Basically I need to execute a list of Rest calls based on the nextUri till I reach to the end ENDDATA.
Is there a preferred way to do this with React & Redux\redux-thunk.
export const find = () => dispatch => {
dispatch({ type: FIND_CUSTOMER_START });
axios({
method: 'post',
url: '/v1/...',
headers: { 'X': 'XXX1' },
auth: {
username: 'XXX1',
password: 'XXX'
},
data: 'SELECT * FROM .....'
}).then(function (response) {
// Need to find the nextUri ... before update teh state
dispatch({
type: SUCCESS,
payload: response.data
});
}).catch(function (error) {
dispatch({ type: ERROR, payload: error });
});
}
Thank you
Define your api call in a redux action
Inside a component lifecycle method you can call this action. As well as map the state from the store to your component.
When the action updates the state it will cause a re-render and call the lifecycle method. In the lifecycle method you can check to see if ENDDATA is true and if not call the action again.
Inside render you can have the same conditional as in the lifecycle method and have it return a spinner to indicate loading.

Set State from ajax call to insert into db outside function?

async componentDidMount(){
// Metrics
await this.forumMetrics();
}
I'm trying to insert data from an API that I'm calling from my ajax call. However, when I try to set state inside the function, it tells me that setstate is not a function. I looked this up and a lot of posts told me I had to .bind(this) which I did but it's still giving me the error.
Futhermore, I also ran into another problem of not being able to access the state from outside the function cause I need to insert it into my service worker that inserts the data into my db. I have access within the function but not outside which I need in order to bind the state to my model in my back-end.
Any suggestions? I feel like I'm close but missing something.
Here is my code:
async forumMetrics(data){
const getuserData = () => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: (data) => {
//console.log(data);
this.setState({
userIp: data.ip,
userCity: data.city,
userRegion: data.region,
userCountry: data.country_name,
userLatitude: data.latitude,
userLongitude: data.longitude
})
//console.log(this.state);
},
})
}
const result = getuserData();
result;
const metricData = {
userIp: this.state.userIp,
userCity: this.state.userCity,
userCountry: this.state.userCountry,
userRegion: this.state.userRegion,
userLatitude: this.state.userLatitude,
userLongitude: this.state.userLongitude,
vieweddateTime: Moment().format('YYYY-MM-DD hh:mm:ss'),
createdDate: Moment().format('YYYY-MM-DD hh:mm:ss'),
year: Moment().format('YYYY'),
month: Moment().format('MM')
}
const metricForum = await MetricService.insertpageView(metricData);
}
You should really not use jQuery to retrieve data. Use fetch() or axios or something like it.
That being said, your problem can be solved by using arrow functions. Instead of function getuserData..., try this:
const getuserData = (result) => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: (data) => {
console.log(data);
// I have access to data in this function
this.setState({
userIp: data.ip
})
},
})
}
Note that the this in the arrow functions is bound to the parent function's this.
Also, remember to bind your forumMetrics function on the component constructor.
EDIT:
I don't see you calling getuserData() anywhere...
EDIT 2:
You might want to make the getUserData a separate functionlike so:
async forumMetrics(data){
const data = await this.getuserData();
console.log(data);
this.setState({
userIp: data.ip,
userCity: data.city,
userRegion: data.region,
userCountry: data.country_name,
userLatitude: data.latitude,
userLongitude: data.longitude
});
}
getUserData(data) {
return new Promise((accept, reject) => {
$.getJSON({
type: 'GET',
url: 'https://json.geoiplookup.io/api?callback=?',
success: accept,
})
});
}
note that the await keyword inside an async function will wait for a promise to finish and use the accepted value to keep going.
You could simplify the getUserData function even more by just using fetch
getUserData() {
return fetch('https://json.geoiplookup.io/api?callback=?');
}
forumMetrics() call
Your forumMetrics function isn't bound, so this is most likely undefined. You can either bind it in the constructor
constructor() {
this.forumMetrics = this.forumMetrics.bind(this);
}
or declare it like:
forumMetrics = async () => {
}
The latter is experimental syntax though.
setState() call
If you go for the first option, inside the callback, your this is definitely not pointing to the class. If you're going to use jQuery for the ajax fetch, then capture the reference in a self variable (which is a bad practice, but so is using jQuery on React):
async forumMetrics(){
const self = this;
function getuserData(result) {
...
self.setState({ ...stuff here... })

ReactJS - mapped array not updating after ajax

I've got a simple component that renders a table. The rows are mapped like this:
render () {
return (
{this.state.data.map(function(row, i){
return <Row row={row} key={i}/>
}.bind(this))}
)
}
The state is initialized in the constructor:
this.state = {
data: props.hasOwnProperty('data') ? props.data : [],
route: props.hasOwnProperty('route') ? props.route : null
}
The data can be initialized in the DOM, or after, the route is passed to the container and bound correctly. In this case, I 'm doing it after in the componentDidMount method:
componentDidMount () {
axios.get(this.state.route)
.then(function(resp){
this.setStateParametersFromAjax(resp);
}.bind(this))
.catch(function(err){
console.log(err);
})
}
The setStateParametersFromAjax(resp) method is defined here:
this.setState({
data: resp.data.data,
});
This all works flawlessly on DOM load. However, there are buttons that will perform subsequent requests later on. These requests perform the same axios call.
The problem is, that even though the state is updated (verified by adding a callback as the 2nd argument to the setState method and logging this.state), the DOM does not update.
What do I need to do to make it so that the DOM updates with this new data as well?
Edit
I had simplified the code a bit, there is a method called fetch() that accepts an argument of params that defines the ajax call:
fetch (params) {
if(typeof params != "object") {
params = {};
}
axios.get(this.state.route, {
params
}).then(function(resp) {
this.setStateParametersFromAjax(resp);
}.bind(this))
.catch(function(err){
console.log(err);
})
}
The componentDidMount() method calls this on load:
componentDidmMount () {
this.fetch();
}
When a button is clicked later, this calls a function that calls the fetch method with parameters:
<li className="page-item" onClick={this.getNextPage.bind(this)}>
Where the function is defined:
getNextPage (event) {
event.preventDefault();
this.fetch({
arg: val
});
}
You should use a unique key value other than index. Even the state is updated, react may not update DOM if the key not changed.
https://facebook.github.io/react/docs/reconciliation.html

ReactJS recieving form errors from server

Below is my signup function in React. How can I render the errors it recieves from backend ? I tried to put a this.setResponse into the catch part ... that didn't work. I understand that componentWillReceiveProps should handle updates but this is an update from a Service (also below) not from a parent component.
If I print the err I can see a dictionary with errors, but I don't see how to pass them to the form or to the fields for rendering.
signup(evt) {
evt.preventDefault();
...
Auth.signup(dict)
.catch(function(err) {
alert("There's an error signing up");
console.log(err.response);
});
},
The Auth service is defined like this:
signup(dict) {
console.log(dict);
return this.handleAuth(when(request({
url: UserConstants.SIGNUP_URL,
method: 'POST',
type: 'json',
data: dict
})));
}
And I hoped to send errors to the fields with the following:
<UserNameField
ref="username"
responseErrors={this.responseErrors}
/>
Presuming that UserNameField is in the same component as the auth callback you can just set the state which will trigger a re-render and pass the values through UserNameField as props.
signup(evt) {
evt.preventDefault();
...
Auth.signup(dict)
.catch((err) => {
alert("There's an error signing up");
console.log(err.response);
this.setState({error: err});
});
},
<UserNameField
ref="username"
responseErrors={this.responseErrors}
error={this.state.error}
/>
This is the same as .bind(this).

Resources