Is there a better way of updating the tx hash? - reactjs

Following is a snippet of my code (which is working)
....
let txhash = '';
class App extends Component {
....
async componentDidMount() {
.....
}
onSubmit = async(event) => {
event.preventDefault();
const accounts = await web3.eth.getAccounts();
this.setState({
message: "Waiting for the transaction to be mined..."
});
await lottery.methods.enterLotteryContract().send({
from: accounts[0],
value: web3.utils.toWei(this.state.value, 'ether')
}).on('transactionHash', function(hash) {
txhash = hash
});
this.setState({
message: "Congrats, the tx has been mined; your txhash is " + txhash
})
console.log(txhash);
});
}
More over here: https://github.com/DKSukhani/lottery-react/blob/master/src/App.js
Was wondering if there is a better way for me to get the txhash and then display it. I was hoping that I could make txhash as one of the state components rather than declaring it as an empty variable. Also I am using the call back function in only reassigning it to txhash.

Can you ask your question a bit more clearly? Your code makes sense and looks fine for some unknown purpose. That purpose is exactly what I need to know though.
When do you need the txHash, is it after every call of this function or are you trying to track something else.
Why can't it be a state variable, you didn't include state in your snipped so I had to go look at the rest of your code and see no reason why not. You use it within the snippet so it's clear you can use it to some degree. I don't use React so maybe there's some render loop, listener issue, or other reason you can't use setState.
With that said, check out this link on the different events you can listen to with on, as I think you're listening to the wrong event or outputting the wrong text. When you set the state's message to say that the tx was mined I don't believe it actually has been.
I would use confirmation and only display the message on the first fire of the event, then ignore the remaining 23. The object returned alongside the confirmation number is described here and has a transactionHash variable you can use to both check that the transaction was confirmed and get the hash at the same time. Now your event call is no longer as wasteful and you can use the same setState call for the message as for the txHash, or even skip storing the txHash at all (as long as you can still use setState asynchronously).

Related

How to emit side effect using new state directly after updating state

I have some code like this:
const [_channel, ably] = useChannel(channelName, msg => {
if (msg.type) === type1 {
setStateVariable(prev => new_from_prev(prev));
// Need the updated state for this
if (condition(stateVariable)) {
// This line attempts to send a socket message
sendMessage(/* someData */);
}
}
})
useChannel is a wrapper around useEffect with dependency array [channelName]; you can see the source code here: https://github.com/ably-labs/react-hooks/blob/main/src/hooks/useChannel.ts
I am not able to access the updated state this way because setStateVariable does the state update asynchronously. This does not work with refs either for the same reason. I am aware that I can use the callback to setStateVariable, however there are several problems with this. First, it violates seperation of concerns and is clearly an anti-pattern. Second, in React Strict Mode, setStateVariable will run twice, sending my socket message twice, which I don't want. The only solution I can think of is putting the code that needs to access the updated state into a setTimeout with an arbitrary timeout length. I have tried this and it works. However, this is obviously a hack and not reliable. There must be a better way to do this. Any help?

RxJS and repeated events

I am new to RxJs in general but am investigating a bug in some React code in which, upon an unrelated action, an old event seems to be emitted and rendered to a display error. Think if you had two buttons that generated two messages somewhere on screen, and clicking one button was showing the message for the other button.
Being new to RxJs I'm not positive where the problem lays. I don't see a single ReplaySubject in the code, only Obserables, Subjects, and BehaviourSubjects. So this is either misuse of an RxJs feature or just some bad logic somewhere.
Anyway I found the code with the related Observable and I'm not quite sure what this person was trying to accomplish here. I have read up on combineLatest, map, and pipe, but this looks like pointless code to me. Could it also be somehow re-emitting old events? I don't see dynamic subscriptions anywhere, especially in this case.
Tldr I don't understand the intent of this code.
export interface IFeedback {
id: number
text: string
}
export interface IFeedbackMessages {
message: IFeedback | undefined
}
feedback$ = new BehaviorSubject<IFeedback | undefined>(undefined)
feedbackNotifs$: Observable<IFeedbackMessages> = combineLatest([
feedback$
]).pipe(
map(([feedback]) => ({
feedback
})
))
I also found this which maybe be an issue. In the React component that displays this message, am I wrong but does it look like each time this thing renders it subscribes and then unsubscribes to the above Subject?
const FeedbackDisplay: React.FC () => {
const [feedbackNotifications, setFeedbackNotifications] = React.useState<IFeedbackMessages>()
React.useEffect(() =>
{
const sub = notification$.subscribe(setFeedbackNotifications)
return () => sub?.unsubscribe()
}, [notifications$])
}
Could it also be somehow re-emitting old events?
Yes, it probably is. BehaviorSubject has the unique property of immediately emitting the last value pushed to it as soon as you subscribe to it.
It's great when you want to model some persistent state value, and it's not good for events whose actual moment of occurrence is key. It sounds like the feedback messages you're working with fall into the second category, in which case Subject is probably a better choice.
does it look like each time this thing renders it subscribes and then unsubscribes to the above Subject?
Not exactly. useEffect accepts a callback, and within that callback you can optionally return a "cleanup" function. React will hang onto that function until the effect is triggered again, then it calls it to clean things up (which in this case consists of closing out the subscription) to make room for the next effect.
So in this case, the unsubscribe will only happen when the component is rendered with a new value for notifications$. Also worth pointing out that notifications$ will only change if it's either passed as a prop or created within the component function. If it's defined outside the function (imported from another file for example), you don't need to (and in fact should not) put it into useEffect's dependency array.

Whether it is good to do lot of functionality in the setState Callback?

I am trying to check and uncheck a checkbox based on other conditions in screen..
I am printing an element using document.getElementById('foo') this is returning null even my element is present in screen and in DOM.
Please help me to solve problem.
I am developing code in which after API is being fetched state variable need to be set and do other functionality based on the respective state variable.
Is it feasible to do most of the logic inside the call back of the setState to promote synchronus way of coding or any other concepts are present to do the same?
this.setState({
filter:filterValue
},function(){
// Most of the coding logic goes here
})
Please suggest a prominent way if it is wrong
Well you definitely shouldn't use document.getElementById since it's against reactive programming logic but it's hard to say where is a problem if you don't provide example code.
Try to implement checkbox in React way:
const CheckBoxComponent = (isChecked) => {
return <CheckBox checked={isChecked ? true : false}/>
}
then in your return:
<CheckBoxComponent isChecked={yourFunctionWhereYouResolveWheneverIsOrNotChecked}/>
Another point is that you really won't to hold logic in setState callback. I guess you are a beginner. You should get better knowledge of functional programming. It's easier than handling state logic and mutation.
Judjing from your question you want probably something like that:
const yourAsyncCallToApi = async() => {
await someApiCall()
yourFunctionWhereYouResolveWheneverIsOrNotChecked() //it will be called as soon as u got data from api call
}
const yourFunctionWhereYouResolveWheneverIsOrNotChecked = () => {
// handle your conditions and return false or true based on them
}

React - Old promise overwrites new result

I have a problem and I'm pretty sure I'm not the only one who ever had it... Although I tried to find a solution, I didin't really find something that fits my purpose.
I won't post much code, since its not really a code problem, but more a logic problem.
Imagine I have the following hook:
useEffect(() => {
fetchFromApi(props.match.params.id);
}, [props.match.params.id]);
Imagine the result of fetchFromApi is displayed in a simple table in the UI.
Now lets say the user clicks on an entity in the navigation, so the ID prop in the browser URL changes and the effect triggers, leading to an API call. Lets say the call with this specific ID takes 5 seconds.
During this 5 seconds, the user again clicks on an element in the navigation, so the hook triggers again. This time, the API call only takes 0,1 seconds. The result is immediatly displayed.
But the first call is still running. Once its finished, it overwrites the current result, what leads to wrong data being displayed in the wrong navigation section.
Is there a easy way to solve this? I know I can't cancel promises by default, but I also know that there are ways to achieve it...
Also, it could be possible that fetchFromApi is not a single API call, but instead multiple calls to multiple endpoints, so the whole thing could become really tricky...
Thanks for any help.
The solution to this is extremely simple, you just have to determine whether the response that you got was from the latest API call or not and only then except it. You can do it by storing a triggerTime in ref. If the API call has been triggered another time, the ref will store a different value, however the closure variable will hold the same previously set value and it mean that another API call has been triggered after this and so we don't need to accept the current result.
const timer = useRef(null);
useEffect(() => {
fetchFromApi(props.match.params.id, timer);
}, [props.match.params.id]);
function fetchFromApi(id, timer) {
timer.current = Date.now();
const triggerTime = timer.current;
fetch('path').then(() => {
if(timer.current == triggerTime) {
// process result here
// accept response and update state
}
})
}
Other ways to handle such scenarios to the cancel the previously pending API requests. IF you use Axios it provides you with cancelToken that you can use, and similarly you can cancel XMLHttpRequests too.

ReactJS fetching new data on prop

As a preface, I'm still new to React, so I'm still fumbling my way through things.
What I have is a component that fetches data to render an HTML table. So I call my Actions' fetchData() (which uses the browser's fetch() API) from within componentWillMount(), which also has a listener for a Store change. This all works well and good, and I'm able to retrieve and render data.
Now the next step. I want to be able to fetch new data when the component's props is updated. But I'm not exactly sure what the proper way to do so is. So I have a three part question
Would the proper place to do my fetchData() on new props be in componentWillReceiveProps(), after validating that the props did change, of course?
My API is rather slow, so it's entirely possible a new prop comes in while a fetch is still running. Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Related to the above question, is there a way to ensure only one fetch is running at any time besides having something like an isLoading boolean in my Action's state (or elsewhere)?
Yes, componentWillReceiveProps is the proper place to do that.
Regarding point 2 and 3:
The idea of cancelling the task and maintaining 'one fetch running' seems to be inadequate. I don't think this kind of solution should be used in any system because implementation would limit an efficiency of your app by design.
Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Why don't you let a 'newer fetch' response override an 'old fetch' response?
If you really want to avoid displaying the old response you can implement it simply using a counter of all fetchData calls. You can implement it in this way:
var ApiClient = {
processing: 0,
fetchData: function(){
processing++
return yourLibForHTTPCall.get('http://endpoint').then(function (response)){
processing--
return response
}
},
isIdle: function(){
return processing == 0
}
}
and the place where you actually make a call:
apiClient.fetchData(function(response){
if(apiClient.isIdle()){
this.setState({
})
}
}
I hope yourLibForHTTPCall.get returns a Promise in your case.

Resources