Not able to trigger function from event - reactjs

I am not a skilled react programmer but still hope someone would care to explain what I am missing:
What I want
I would like to change accounts in Metamask, detect the "accountsChanged" event, and trigger the testFunction.
What works
I am able to trigger the testFunction by clicking the test function button.
I can detect account change (for some reason it is detected around 5 times every time I change).
What does not work
I am not able to trigger the testFunction upon account change and get the message TypeError: this.testFunction is not a function
Suspect there is something fundamental about react I am missing here...Thanks for all replies!
class App extends Component {
...
componentDidMount = async () => {
...
};
testFunction = async =>{
console.log("triggered the test function");
};
render() {
window.ethereum.on('accountsChanged', function (accounts) {
console.log("account change detected");
this.testFunction(); --> this is not working
});
return (
<div className="App">
<button type="button" onClick={this.testFunction}>test function</button>
</div>
);
}
}

You need to convert your normal function to arrow function. Because normal function derives this from the object which is calling it, but arrow function derives it's this from surrounding scope, hence in arrow function this will point to your class and will have access to the methods.
window.ethereum.on('accountsChanged', accounts => {
Also, you can continue using normal function, but in that case you can store the this in some other variable like that' or 'self and use it inside the normal function to call the methods of the class.
let that = this;
window.ethereum.on('accountsChanged', function(accounts){
that.testFunction() //this will work

I struggled to update the component of my app when an account was changed using MetaMask. What I did was what Vivek suggested: create a reference of this and then handle the callback. At the end my function using etherjs and the same event of metamask (ethereun.on('accountsChanged'..was this
const here = this
provider.provider.on('accountsChanged', function (accounts) {
console.log('Account changed!!')
here.currentAccount = accounts[0]
})
This code also work with Vue

Related

Initiate function on page load

I have a function that gets some data when it is initiated. This can be done by a click even on a button, e.g.:
<button type="button" onClick={fetchData}>Get data</button>
Basically it is just a function given by:
function fetchData(e) {
e.preventDefault();
<something POST to get data>
}
All of this is working as intended. However, the data is only fetched when the button is clicked. I would like for the data to be fetched on load as well so there is actually some data presented before the button is clicked.
I thought I could just say:
useEffect(() => {
fetchData()
}, [])
But that doesn't seem to work. However, if I take everything inside the function, and replace it with the fetchData() in the useEffect, then it works. But I don't feel like changing stuff twice every time the function needs updates etc.
So what am I doing wrong ?
That is because when you're calling fetchData() in useEffect, no argument is being provided to the function. Yet in the function you are expecting the first argument to be present and calling it with e.preventDefault(). Since e will be undefined, attempting to access a key/property inside undefined will throw an error.
There are two solutions:
Only invoke functions stored in e when it is not undefined
Write a separate handler for the click event binding
Solution 1: Use optional chaining
By first checking if e is null or undefined, we can prevent attempting to access a key/property on it:
function fetchData(e) {
e?.preventDefault();
// Additional fetching logic
}
If you want to also support browsers that do not support optional chaining, then using the logical AND operand (&&) to perform lazy evaluation / short-circuiting will also work:
function fetchData(e) {
e && e.preventDefault();
// Additional fetching logic
}
Solution 2: Separate event handlers for button
Remove e from your fetchData function completely:
function fetchData() {
// Additional fetching logic
}
...and prevent the default event at the level of the click handler:
const onClick = (e) => {
e.preventDefault();
fetchData();
}
<button type="button" onClick={onClick}>Get data</button>

Accessing this object in componentdidmount

I'm having a problem with accessing/triggering functions from componentDidMount in React. All this references seem to be undefined even if I try binding the method in the constructor like this:
this.componentDidMount = this.componentDidMount.bind(this);
Here is a part of the code; I'm accessing events on leaflet maps and printing map boundaries to the console - which works fine (and that's the reason I need to use componentDidMount)
componentDidMount(){
let mapInst = this.refs.map.leafletElement;
mapInst.on('moveend', function () {
console.log(mapInst.getBounds())
});
mapInst.on('dragend', function () {
console.log(mapInst.getBounds())
});
}
Now I would like to pass those boundaries to state parameters or launch a function on callback to a parent element.
updateParent = (newBounds) => {
this.props.parentCallback({'bounds': newBounds});
}
However whatever construction I try, any function in higher scope is always undefined. It seems I cannot access neither updateParent(mapInst.getBounds()) nor this.updateParent(mapInst.getBounds()) from within componentDidMount.
Does anybody have any idea what the optimal construction in such cases is?
Full code for reference: https://pastebin.com/gQqazSCs
I think you want to use leaflet's whenReady callback.
handleMapReady = ({ target }) => {
this.setState({ leafletMapElt: target });
};
...
<Map whenReady={this.handleMapReady}>...</Map>
I don't think the map is guaranteed to be in a "ready" state in componentDidMount
Make callback function of mapInst.on to arrow function and then try to access updateParent like this mapInst.on('moveend', ()=> { this.updateParent(mapInst.getBounds()) });
Arrow function will take its surrounding this

Understanding the difference between this.function() and this.function

Hi I have been reading on react as well as doing some coding and I can't help but notice that in certain portions of the code we will use this.function() and in some we will call using this.function, I do not understand what is the difference between them and how do i determine when should i call with () and when should i not.
For example I can have the following code which will use this.function
//Arrow function used to bind the necessary variables
CallFunction = (event) =>{
console.log("Event was called");
}
render(){
return(
<form onSubmit={this.CallFunction} />
);
}
Next I can have the following code
CallFunction () {
console.log("Event was called");
}
render(){
return(
<div>{this.CallFunction()}</div>
);
}
This is just plain ole javascript, it's the difference between executing a function and just referencing a function. If you have the parens, that function will get executed immediately upon render. So in your second example you would see 'Event was called' immediately in your console when this component mounts. There are several reasons why we might have to do this, a simple one is that we just want to break out some of our code to a new function to make things easier to read.
With callbacks we don't want to execute the function right away, so we don't use the parens. We are just referencing the function. We're basically saying here's the function I want you to execute when someone submits this form. If we used the parens, that function would execute immediately on the component mounting - not wait until the form is submitted.
edit: Sometimes you need to have a function execute on render and then return a new function you want to execute as an event handler callback:
function thisRunsOnRender () {
return function thisRunsOnSubmit (e) {
console.log(e) // this would be the submit event
}
}
....
render () (
<form onSubmit={thisRunsOnRender()} />
)

Reactjs function not being called

I'm implementing the Facebook SDK on my react project, however, I am new to React and I still don't grasp some concepts. I'm calling handleFBLogin when a user clicks on a button. This function then calls checkLoginState to continue my code's logic. I've already bound checkLoginState in the constructor using:
this.CheckLoginState = this.checkLoginState.bind(this);
I call this function on handleFBLogin, but checkLoginState doesn't seem to be called. I can see the yes on my console:
handleFBLogin() {
console.log("yes")
this.checkLoginState;
}
Here's the checkLoginState function:
checkLoginState(){
console.log("checloginstate")
window.FB.getLoginStatus(function(response) {
console.log(response);
this.statusChangeCallback(response);
}.bind(this));
}
Why isn't it being called?
It's possible you think to call the function without using parentheses as you do in the event like onClick={this.yourMethod}. It works because the react will handle it internally.
But calling a function from a method to another, you need to call the function using parentheses:
this.checkLoginState()
But wait! this will be undefined here if have not bind this. So, bind this inside your constructor:
this.handleFBLogin = this.handleFBLogin.bind(this)
Alternatively, you may use public class method:
handleFBLogin = () => {
this.checkLoginState()
I think it is type error. You forgot to add '()' for calling a function.
handleFBLogin() {
console.log("yes")
this.checkLoginState();
}
inside handleFBLogin() try () => this.checkLoginState(); . You need the parenthesis to invoke the method.

Throttling dispatch in redux producing strange behaviour

I have this class:
export default class Search extends Component {
throttle(fn, threshhold, scope) {
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
}
render() {
return (
<div>
<input type='text' ref='input' onChange={this.throttle(this.handleSearch,3000,this)} />
</div>
)
}
handleSearch(e) {
let text = this.refs.input.value;
this.someFunc();
//this.props.onSearch(text)
}
someFunc() {
console.log('hi')
}
}
All this code does it log out hi every 3 seconds - the throttle call wrapping the handleSearch method takes care of this
As soon as I uncomment this line:
this.props.onSearch(text)
the throttle methods stops having an effect and the console just logs out hi every time the key is hit without a pause and also the oSearch function is invoked.
This onSearch method is a prop method passed down from the main app:
<Search onSearch={ text => dispatch(search(text)) } />
the redux dispatch fires off a redux search action which looks like so:
export function searchPerformed(search) {
return {
type: SEARCH_PERFORMED
}
}
I have no idea why this is happening - I'm guessing it's something to do with redux because the issue occurs when handleSearch is calling onSearch, which in turn fires a redux dispatch in the parent component.
The problem is that the first time it executes, it goes to the else, which calls the dispatch function. The reducer probably immediately update some state, and causes a rerender; the re-render causes the input to be created again, with a new 'throttle closure' which again has null 'last' and 'deferTimer' -> going to the else every single time, hence updating immediately.
As Mike noted, just not updating the component can you get the right behavior, if the component doesn't need updating.
In my case, I had a component that needed to poll a server for updates every couple of seconds, until some state-derived prop changed value (e.g. 'pending' vs 'complete').
Every time the new data came in, the component re-rendered, and called the action creator again, and throttling the action creator didn't work.
I was able to solve simply by handing the relevant action creator to setInterval on component mount. Yes, it's a side effect happening on render, but it's easy to reason about, and the actual state changes still go through the dispatcher.
If you want to keep it pure, or your use case is more complicated, check out https://github.com/pirosikick/redux-throttle-actions.
Thanks to luanped who helped me realise the issue here. With that understood I was able to find a simple solution. The search component does not need to update as the input is an uncontrolled component. To stop the cyclical issue I was having I've used shouldComponentUpdate to prevent it from ever re-rendering:
constructor() {
super();
this.handleSearch = _.throttle(this.handleSearch,1000);
}
shouldComponentUpdate() {
return false;
}
I also moved the throttle in to the constructor so there can only ever be once instance of the throttle.
I think this is a good solution, however I am only just starting to learn react so if anyone can point out a problem with this approach it would be welcomed.

Resources