Is there any difference between both the button click event in the given component? Which is the preferred way to write?
export default class App extends Component {
doSomething = () => {
console.log('Hi');
}
render() {
return (
<Container>
<Button onClick={this.doSomething} title="Test" />
<Button onClick={() => this.doSomething()} title="Test" />
</Container>
);
}
}
When you don't need to pass the parameter, you can just use
{this.doSomething}
But if you need to pass the parameter to the function, then this will cause your method to execute immediately:
{this.doSomething(param)}
Thus, not to execute the function immediately, we need to use arrow method like you used:
{() => this.doSomething(param)}
Thus, in your case both are same. Because they are only executed when onClick is called ie. you click on the element.
Bonus:
You can still use the first way even you want to pass the parameter:
{this.doSomething(param)}
But for this, you need to define your method like this:
doSomething = (param) => () => {
console.log('Hi');
}
Furthermore, if you wish to use event object, then you may use like below:
doSomething = (param) => (event) => {
console.log('Hi');
}
Or, with the second approach ie. with arrow function:
{(event)=>this.doSomething(event,param)}
And obviously, if you are worried about performance, I would suggest not to use inline arrow function. The preferred way to use:
doSomething = (param1, param2,) => (event) => {
Misunderstanding:
Some people might find the method that pass the parameter without using inline arrow function will also work. But this is incorrect. Let me clarify on this.
When you use {this.doSomething(param)}, this function seems to work fine with its parameter. But if you change the state inside the handler, then you'll know the big difference. You'll get error maximum update depth exceeded react.
But with the same, you can avoid that error and also avoid the performance issue, you'll need to define the method like I stated before with arrow function:
doSomething = (param) => () => {
From doc
<Button onClick={() => this.doSomething()} title="Test" />
The problem with this syntax is that a different callback is created
each time the Button renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering.
<Button onClick={this.doSomething} title="Test" />
We generally recommend binding in the constructor or using the class
fields syntax, to avoid this sort of performance problem.
First we will look when to use both:
onClick={this.doSomething} : This is using class variable directly, but it cannot be used when we are required to pass parameters to the function. For that, the second way comes to rescue.
A way to pass parameters to this is :
onClick={this.doSomething.bind(params, this)}
onClick={() => this.doSomething()}: you can pass parameters to the function as
onClick={() => this.doSomething(param1, param2)}.
Also, an important point to note, when the component re-renders, memory is allocated for the second one every time, while the first one just uses memory reference. So, first one is better if you don't have to pass parameters in the function.
Related
This is somewhat of weird question. I'm working with event types in React, and we want to use onClick in some instances, and onPointerDownCapture in others (for reasons). But to make this more general, it could be any two different click-like events. The issue is that while we can assign whatever function handler on the right side of the expression, the left side has to be static, essentially. So,
<button
onClick={handler} vs onPointerDownCapture={handler} vs onMouseDown={handler}
/>
I think just using onPointerDownCapture will be fine for most usecases, but in a perfect world, I'd be able to flip between these at runtime based on other variables. Is it possible to override the onClick on the button/div/whatever prototype or something to be whatever event type I want it to be?
Much googling. No success.
I didn’t fully understand what you mean by “overriding onClick”, but
The issue is that while we can assign whatever function handler on the right side of the expression, the left side has to be static, essentially.
This is not true, left hand side could be dynamic, here’s how:
<button {...({ [eventName]: handler })} />
I guess this solves your problem.
Ok above syntax is a bit terse and admittedly confusing. It’s the good old JSX spread props syntax, just over an inline object literal.
I’ll give you another equivalent form, hopefully it should be more readable.
const eventName = someCondition ? "onPointerDownCapture" : "onClick"
const props = {
[eventName]: handler
}
<button {...props} />
You have to use those attribute names and you use the same function name for all 3 of them.
What these 3 attributes do is they register the associated event.
Maybe you could use a useEffect and add there conditionally an event listener instead of the proposed React attributes.
I think best is #vera solution in comment. Pass extra prop to component (for example isOnClick), and based on it pass either callback or undefined to event handler prop:
function Component(props: { isOnClick: boolean; callback: () => void }) {
return (
<div
onClick={props.isOnClick ? props.callback : undefined}
onMouseDown={props.isOnClick ? undefined : props.callback}
/>
);
}
Note that passing undefined to prop is same as not setting that prop.
Alternatively conditionaly return component:
function Component(props: { isOnClick: boolean; callback: () => void }) {
if (props.isOnClick) {
return <div onClick={props.callback}/>
} else {
return <div onMouseDown={props.callback}/>
};
}
In React, I can call the function in onClick in two different ways.
First Method: Arrow Function
const Modal = ({isOpen, toggle, children}) => {
<div onClick={() => toggle()}>
<div>
{children}
</div>
</div>
}
Second Method: Without brackets
const Modal = ({isOpen, toggle, children}) =>
{
return(
<div onClick={toggle}>
</div>
)
}
What is the difference between them? () => toggle() <-> toggle
The first way, onClick={() => toggle()}, creates a new function and assigns it to the onClick property. That function, when called, will call toggle with no arguments. A new function is created every time that component is rendered.
The second way, onClick={toggle}, does not create a new function, it directly assigns toggle to the onClick property. That means that when it's called, it receives any arguments that are passed (even if it doesn't expect any).
Each can be appropriate, depending on what you want to do. If toggle expects the arguments the click event will pass it, you're better off with the second way since you aren't creating a new function every time. If it doesn't, in general it's best not to set it up to receive an argument it doesn't expect.
The fact that the first way creates a new function on every render probably doesn't matter when you do this with a DOM element like a div, but suppose you're passing a function to a complex component that takes time to render and that optimizes render (avoiding re-rendering if its props don't change, via React.memo, PureComponent, shouldComponentUpdate, etc.):
return <SomeComplexComponent onSomething={() => toggle()} />
In that case, you might be best off "memoizing" the function (in this case, via useCallback or useMemo, usually) so you don't pass a new one to the component every time, so the complex component doesn't think it needs to re-render every time.
const onSomething = useCallback(() => toggle(), [toggle]);
// ...
return <SomeComplexComponent onSomething={onSomething} />
Although that still creates a new function on every render (so it can be passed into useCallback), useCallback will return the previous version of the function if toggle hasn't changed, which allows SomeComplexComponent to avoid re-rendering.
But there's no need for that when passing this to an HTML element or a simple component.
1st kind of code
onClick={toggle}
In this kind of onClick method you can call only one function inside onClick.
2nd kind of code
Suppose you need to call multiple functions inside the onClick. Then you have to use
onClick={() => toggle()}
Exmaple:-
OnClick = {()=>{
toggle1();
toggle2();
}}
And also you can pass parameters inside the function. using 2nd type like below.
Example: -
OnClick = {()=>{
person(name);
}}
In () => toggle() you are adding an anonymous function and then calling toggle function. If you want to pass in some arguments to toggle function, you can use this method. eg () => toggle(id, name..etc)
The other method simply add event handler and that function will receive event as argument.
Is there any difference between both the button click event in the given component? Which is the preferred way to write?
export default class App extends Component {
doSomething = () => {
console.log('Hi');
}
render() {
return (
<Container>
<Button onClick={this.doSomething} title="Test" />
<Button onClick={() => this.doSomething()} title="Test" />
</Container>
);
}
}
When you don't need to pass the parameter, you can just use
{this.doSomething}
But if you need to pass the parameter to the function, then this will cause your method to execute immediately:
{this.doSomething(param)}
Thus, not to execute the function immediately, we need to use arrow method like you used:
{() => this.doSomething(param)}
Thus, in your case both are same. Because they are only executed when onClick is called ie. you click on the element.
Bonus:
You can still use the first way even you want to pass the parameter:
{this.doSomething(param)}
But for this, you need to define your method like this:
doSomething = (param) => () => {
console.log('Hi');
}
Furthermore, if you wish to use event object, then you may use like below:
doSomething = (param) => (event) => {
console.log('Hi');
}
Or, with the second approach ie. with arrow function:
{(event)=>this.doSomething(event,param)}
And obviously, if you are worried about performance, I would suggest not to use inline arrow function. The preferred way to use:
doSomething = (param1, param2,) => (event) => {
Misunderstanding:
Some people might find the method that pass the parameter without using inline arrow function will also work. But this is incorrect. Let me clarify on this.
When you use {this.doSomething(param)}, this function seems to work fine with its parameter. But if you change the state inside the handler, then you'll know the big difference. You'll get error maximum update depth exceeded react.
But with the same, you can avoid that error and also avoid the performance issue, you'll need to define the method like I stated before with arrow function:
doSomething = (param) => () => {
From doc
<Button onClick={() => this.doSomething()} title="Test" />
The problem with this syntax is that a different callback is created
each time the Button renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering.
<Button onClick={this.doSomething} title="Test" />
We generally recommend binding in the constructor or using the class
fields syntax, to avoid this sort of performance problem.
First we will look when to use both:
onClick={this.doSomething} : This is using class variable directly, but it cannot be used when we are required to pass parameters to the function. For that, the second way comes to rescue.
A way to pass parameters to this is :
onClick={this.doSomething.bind(params, this)}
onClick={() => this.doSomething()}: you can pass parameters to the function as
onClick={() => this.doSomething(param1, param2)}.
Also, an important point to note, when the component re-renders, memory is allocated for the second one every time, while the first one just uses memory reference. So, first one is better if you don't have to pass parameters in the function.
<div id="board" onClick={(event) =>
this.props.boardClicked(event)}>
I would like to pass a second argument trying something like this, but my syntax is off I think:
<div id="board"
onClick={(event, {this.props.activeCharacter}) =>
this.props.boardClicked(event, {this.props.activeCharacter})}>
Simple change would be that because you are using an arrow function it is lexically bound automatically to the react compmponent instance, so you can use this within function. Simply pass as an extra arg from within the function
<div id="board" onClick={(event) => this.props.boardClicked(event, this.props.activeCharacter)}>
Using arrow functions in event handlers in react is a bad idea though because it creates an anonymous function, that will cause this component to unnecessarily always re-render which can hurt performance if component is used a lot
You can overcome that by defining a handler on your class
onBoardClick = (event) => this.props.boardClicked(event, this.props.activeCharacter)
render = () => <div id="board" onClick={this.onBoardClick} />
Now you are passing the same function reference each time so no unnecessary re-renders but same click handler functionality
I am trying to move over the Auth0 login function as described in their tutorial. I am able to get it work if I use it like this:
<button className="btn" onClick={this.props.route.auth.login.bind(this)}>test</button>
but if I set up the button to call a function I define above the render function like this:
login() {
this.props.route.auth.login.bind(this);
}
And change the onclick to be like this:
onClick={this.login()}
or
onClick={() => this.login()}
Then the auth login modal never opens and i receive no error. Also i added a console.log to login() and I can see it in the console, but the actual login modal never opens? It works in the first example, but not in the others.
The reason I am attempting to move this into a function is because I would like to pass the login function down into a child component later, and I was unable to do so and I believe this to be the root issue thats preventing me.
bind does not call your function:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. docs
Also, you are setting the value of onClick prop to the return value of login. If you want to pass a reference to the function, you have to do it without the ().
Your code should look like this:
<button className="btn" onClick={() => this.login()}>test</button> <!-- You need to keep a reference to `this`, hence the binding -->
Then:
login() {
this.props.route.auth.login();
}
I edited the answer so that it uses an arrow function. However, I prefer not doing that, since it makes the code a bit cumbersome, and rather bind all the functions in the constructor, like #patrick-w-mcmahon did.
Let's say you have a container MyContainer and this container renders a view called MyView. This view has a button that calls a method. MyContainer is going to pass to the MyView the method it needs to use.
MyContainer:
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.myFunc = this.myFunc.bind(this);
}
myFunc() {
console.log("hello world");
}
render() {
return <MyView myClick={this.myFunc}/>;
}
}
MyView:
const MyView = ({ myClick }) => {
return <button onClick={myClick} />;
};
MyView.propTypes = {
myClick: PropTypes.func
};
export default MyView;
You pass the needed function from the container to the view and the view calls its parents function from props. the use of bind() sets this scope to the current scope so that when you call this from a different scope it is going to be the scope of the bind. When you are in the render you run a different scope so you must bind your functions to the current class scope so that this.myReallyCoolFunction() is pointing to the correct scope (your class scope).
.bind() will only bind the object and arguments but won't call (run) the function.
TL;DR Just use .call() instead of .bind()
instead of .bind() you can use
.call(this, args) which is basicaly the same as bind only that call will call (run) the function.
you could also use .apply(), which is basicaly the same as .call() but takes an array with the arguments instead of object like .call()
this way you can avoid arrow functions in you jsx render()
and kind of keeping the line of thought with react.
something like ->
login() {
this.props.route.auth.login.call(this);
}
When you call props function through return(JSX) React takes care of calling it once propagation ends.