Overriding onClick in React - javascript

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}/>
};
}

Related

React TypeScript callback reading undefined when using setState, but it will log the value

I am writing a react application, and for some reason I can not wrap my head around this issue. Below is a snippet, the only thing that should be needed:
onFirstNameChange(event: any){
console.log(event.target.value)
// this.setState({
// firstname: event.target.value
// })
}
The commented out code will not run, it says it can not read properties of undefined. However when I log the events value it does it perfectly. Any ideas on why this is happening? It is an onchange event. It is also deeply nested, however the value does make it back.
React components written in an ES6 class, do not autobind this to the component's methods. There are 2 solutions primarily. You may use choose either:
Either explicitly bind this in constructor
constructor(props) {
super(props);
// rest of code //
this.state = {
firstname: '',
};
// rest of code //
this.onFirstNameChange = this.onFirstNameChange.bind(this);
}
Or use ES6 Arrow Function
onFirstNameChange = (event: any) => {
console.log(event.target.value);
this.setState({
firstname: event.target.value
});
}
As Brian stated, I also believe the error saying that it cannot read property of undefined, is likely saying it cannot read property setState of undefined, because "this" is undefined.
This is most likely caused by providing the onFirstNameChange handler without leveraging a closure, bind, or arrow function to bind the value of this to the "this" value you are expecting.
My guess is your code leveraging the on change handler looks like the following:
<input type="text" value={this.state.value} onChange={this.onFirstNameChange} />
You can refer to the "Handling Events" page (link here) of the React documentation, you will find the following about midway down the article:
You have to be careful about the meaning of this in JSX callbacks. In
JavaScript, class methods are not bound by default. If you forget to
bind this.handleClick and pass it to onClick, this will be undefined
when the function is actually called.
This is not React-specific behavior; it is a part of how functions
work in JavaScript. Generally, if you refer to a method without ()
after it, such as onClick={this.handleClick}, you should bind that
method.
If calling bind annoys you, there are two ways you can get around
this. If you are using the experimental public class fields syntax,
you can use class fields to correctly bind callbacks.
SOLUTIONS: I've included examples of the 3 possible solutions below to bind the value of this, depending on your preference:
Option 1 - Assuming this is a class based component, which I am based on the syntax shown, you can bind the method in the constructor:
constructor(props) {
super(props);
this.onFirstNameChange.bind(this);
}
Option 2 - Update method definition to public class fields syntax with an arrow function to bind "this": (See here)
onFirstNameChange = (event: any) => {
console.log(event.target.value)
this.setState({
firstname: event.target.value
})
};
Option 3 - Update onChange callback with anonymous arrow function to bind this value:
<input type="text" value={this.state.value} onChange={() => this.onFirstNameChange} />
Note on option 3 from React Docs:
The problem with this syntax is that a different callback is created
each time the LoggingButton 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. We generally
recommend binding in the constructor or using the class fields syntax,
to avoid this sort of performance problem.

What's the difference between the two ReactJS click function calls [duplicate]

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.

What is the difference between both button click in the given React Component?

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.

Binding no-ops or null handlers to Vue event handlers?

Is there a Vue convention or best practice for defining empty event handlers?
Vue's transition element includes the convenient, though easily overlooked, appear attribute. It uses the transition's enter hooks by default, but a separate set of appear hooks also becomes available. I have a case where :appear should do nothing while :enter triggers a function. If :appear is not bound, Vue falls back to :enter, so something has to be there.
Writing conditional logic into the enter hook feels wrong, but I'm not sure how best to define the hook. The following all work, but if there isn't a Vue convention, which should be considered a best practice?
Boolean attribute
<transition
appear
v-on:appear
v-on:enter="doEnter"
>
"Empty" attribute
<transition
appear
v-on:appear=""
v-on:enter="doEnter"
>
Explicit no-op handler
<transition
appear
v-on:appear="doAppear"
v-on:enter="doEnter"
>
new Vue({
//...
methods: {
//...
doAppear: () => {}
}
});
Some other options could include inline no-op functions v-on:appear="() => {}" or abusing JavaScript's "everything is a function" nature with an empty object v-on:appear="{}" or number v-on:appear="0". false doesn't work, because Booleans aren't functions and Function doesn't work because Vue sees a string instead of the bare prototype.
I don't think there is much of a noticeable difference unless you benchmark it.
If this matters to you, you could choose to not have the appear handler by passing a dynamic object to v-on instead.
<transition ... v-on="transitionEventHandlers">
JS
data: {
shouldUseAppearHandler: true
},
computed: {
transitionEventHandlers() {
let handlers = {
enter: this.doEnter
};
if(this.shouldUseAppearHandler) {
handlers = {
appear: this.doAppear,
...handlers
}
}
return handlers;
}
},
methods: {
doEnter() {
console.log("enter");
},
doAppear() {
console.log("appear");
}
}
example: https://codepen.io/jacobgoh101/pen/aGVzjR?editors=1011

React jsx not adding event listener to the element

I have a react-redux app which requires a part which is conditional.
i.e. it can either be a div with a certain className or a div with the same className and an onClick handler which fires an action. so I created a function which returns jsx according to the above conditions.
Now the problem is that the onClick is not being added to the props as I expected but className is working fine.
class someClass {
renderSomething(){
return <div className="someClass" onClick={() =>
this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
What I expected it to be
<div className="someclass" onClick={() => this.props.someAction()}>Something</div>
What Rect dev tools show
<div className="someclass">Something</div>
Don't know where I went wrong.
Edit 1: The function was mistakenly written outside the class.
You have a typo. In your renderSomething() method, you should have the following:
renderSomething() {
return (
<div className="someclass" onClick={this.props.someAction}>Something</div>
);
}
EDIT: Now I saw that renderSomething was not a part of class from it was called from. In order to keep reference to this, you have to move it inside of your class, hence make it class method.
If you wish to add an argument to someAction function, I suggest you add a event handler to your React component, and then assign it to a onClick. Using arrow function in render method causes creation of a new function in memory every time component re-renders.
Maybe you meant to do this?
class someClass {
renderSomething(){
return <div className="someClass" onClick={() => this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
In your original example, renderSomething wasn't a part of someClass -- so calling this.renderSomething() would have thrown a runtime error.
I could be wrong, but i think
render(){
return {this.renderSomething()}
}
Is the way to go. Let me know if render(){this.renderSomething()} works.

Categories