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()} />
)
Related
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>
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
One thing that confuses me is the onChange event.
As the code below, onChange event work with handleChange function.
So what confuses me here is the event parameter.
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "empty"
};
// change code below this line
this.handleChange = this.handleChange.bind(this);
// change code above this line
}
// change code below this line
handleChange(event) {
this.setState({
input: event.target.value
})
}
// change code above this line
render() {
return (
<div>
{ /* change code below this line */}
<input type="text" value={this.state.input} onChange={this.handleChange}></input>
{ /* change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
Does every function work with onChange event need to have a parameter. If the answer is yes, why?
I replaced the keyword ‘event’ with some other keywords and it still works normally. So is it okay if I use a different word instead of the keyword ‘event’?
Does every function work with onChange event need to have a parameter. If the answer is yes, why?
They don't need to use the event parameter, therefore you don't need to declare that parameter in the callback function and the function will get called anyways (the invoker will still pass the parameter, of course, but you can ignore it if you don't need it). However, if you want to know what has changed, then you probably will want to use the info of the event parameter, right?
I replaced the keyword ‘event’ with some other keywords and it still works normally. So is it okay if I use a different word instead of the keyword ‘event’?
The word event is not a reserved word in JavaScript, you can name the parameter of the function however you want, it will still work just the same, yes.
Maybe the following example will help you understand how callbacks work with JS.
Consider the following function... What it does is it lets you register a function that will get invoked every certain number of milliseconds. Whenever the function gets invoked you will receive the current epoch time:
const timerSubscription = (ms, fn) => {
const id = setInterval(() => {
fn(Date.now());
}, ms);
return () => clearInterval(id);
}
So you could use it like this:
let count = 0;
const unsubscribe = timerSubscription(1000, () => {
count++;
console.log('One second passed');
if (count === 5) {
unsubscribe();
}
});
Or you could use it like this:
let start = Date.now();
const unsubscribe = timerSubscription(1000, (now) => {
console.log('One second passed');
if (now + 5000 >= start) {
unsubscribe();
}
});
They do almost exactly the same thing, one uses the argument that the invoker passes to it and the other one doesn't. Either way the invoker is always passing the argument.
I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}
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.