What is the reason that function is not passed to a Button - javascript

I'm aware of JavaScript's scopes but probably I don't understand them fully because this code doesn't work.
This code uses React and Relay Modern frameworks.
There are 2 buttons, first one inside queryRender which is passed into Relay Modern QueryRenderer and second one afterwards (see function render). The second one is working, first one doesn't execute the clickTest function.
(This is simplified version of actual code)
class Candidates extends Component {
static propTypes = {
viewer: PropTypes.object
}
constructor (props) {
super(props)
this.clickTest = this.clickTest.bind(this)
}
clickTest () {
console.log('click works')
}
queryRender ({error, props}) {
if (error) {
return <pre>{error.message}</pre>
} else if (props) {
return (
<div>
<Button onClick={this.clickTest}>this DOESN'T work</Button>
</div>
)
}
return <Loader active>Loading...</Loader>
}
render () {
return (
<div>
<QueryRenderer
environment={environment}
query={query}
render={this.queryRender}
/>
<Button onClick={this.clickTest}>this works</Button>
</div>
)
}
}
The query variable is defined, I just didn't include it in that excerpt.
When I substitue first button's onClick function with an anonymous one
<Button onClick={() => this.clickTest()}>this DOESN'T work</Button>
then I get such error: Uncaught TypeError: _this2.clickTest is not a function
Can anyone explain to me why this code behaves the way it does?

In javascript, the meaning of this isn't determined when a function is created, but rather when it is invoked. When QueryRenderer invokes your queryRender function, it doesn't know that it needs to invoke it in the context of your class, so this will not be referring to what you think it's referring to.
You'll either need to bind your queryRender function, much like you're doing with your clicktest function in the constructor, or you'll need to redesign queryRender so it doesn't need a reference to this.

To expand upon both Artur and Nicholas' answers, you either need to bind() this or use an arrow function to make sure that this is referring to the component itself. You already have the bind method down, here's en example of the arrow function which gets rid of the need to bind because arrow functions don't actually bind a this value, they use their parents scope instead...
class Candidates extends Component {
static propTypes = {
viewer: PropTypes.object
}
constructor (props) {
super(props)
this.clickTest = this.clickTest.bind(this)
}
clickTest () {
console.log('click works')
}
queryRender = ({error, props}) => {
if (error) {
return <pre>{error.message}</pre>
} else if (props) {
return (
<div>
<Button onClick={this.clickTest}>this DOESN'T work</Button>
</div>
)
}
return <Loader active>Loading...</Loader>
}
render () {
return (
<div>
<QueryRenderer
environment={environment}
query={query}
render={this.queryRender}
/>
<Button onClick={this.clickTest}>this works</Button>
</div>
)
}
}

Arrow function doesn't create new scope and its scope is enclosing execution context, in this case it's QueryRenderer scope where you don't have this function. When you pass it as simple function then the scope will be undefined or not, I don't know what Button does inside. I haven't used Rely and not sure you can refer to component from Rely render method.

Related

pass parameters to a javascript function without using an inline function inside jsx

So after reading a few articles on react native performance, I realized that you're supposed to avoid arrow functions inside JSX.
https://blog.codemagic.io/improve-react-native-app-performance/#avoid-arrow-functions
But I have the following react-native code
function Layout() {
function handlePress(index) {
console.log("Inside HandlePress", index)
// I want index here
}
function NavItemIterable(item, index) {
return(
// how to pass index without arrow functions
<Pressable key={index} onPress={handlePress}>
<NavItem />
</Pressable>
)
}
return(
<Box>
{data.map(NavItemIterable)}
</Box>
)
}
With arrow functions, I could do something like
<Pressable key={index} onPress={()=> handlePress(index)}>.
How can I avoid an arrow function is this case & still call handlePress with index.
(or should I even try to avoid)
That blog post you have mentioned is at least misleading, and doesn't give sufficient context to understand it's statement (e.g. why you would even use a nested function in that case at all).
Your example (a function component) is fundamentally different to the one in that blog post (a class component).
In your example, it is ok to use a function to "pass" the parameters to the handler.
That is generally inline with common react principles, and it doesn't matter a lot if you are using
an arrow function (const f = () => {};) or a classic function (const f = function(){};). (optimizations are still possible.)
What the blog post says, and doesn't say ...
The blog post states that you should "Avoid Arrow Functions", and gives an example.
In that example there is a difference between using a classic and an arrow function, but that isn't the actual problem,
and the blog post doesn't say why you would use an extra function there at all (no matter if classic or arrow function) ?
class MyClass extends React.Component {
addTodo() {}
render() {
return <>
<MyComponent onClick={ () => this.addTodo() /* <-- Why would you even do this ? ... */ } />
<MyComponent onClick={ function(){ this.addTodo() } /* <-- ... even with a classic function ? */ } />
<MyComponent onClick={ this.addTodo } /> /* <-- Recommended. */
</>;
}
}
Of course, you should avoid that extra function, if there is no reason to add it.
What the blog post probably means ...
A common reason to use an extra function there is to capture the this context of the class. Indeed, here you need to use an arrow function.
In this example, only the arrow function will work (and the alternative), because it captures the this context of the component:
class MyClass extends React.Component {
myValue = 'some value';
addTodo(){
try { console.log( 'ok, value is:', this.myValue ); }
catch(_){
console.error( 'error: `this` is:', this );
}
}
render() {
const capturedThis = this;
return <>
<button onClick={ () => this.addTodo() } > arrow: works. </button>
<button onClick={ this.addTodo } > no extra function: error. </button>
<button onClick={ function(){ this.addTodo(); } } > classic function: worse error! </button>
<button onClick={ function(){ capturedThis.addTodo(); } } > classic alternative: works. </button>
</>;
}
}
What the blog post missed ...
The captured this context ("closure") might cause problems. That is probably why the blog post recommends to avoid it.
But the actual recommendation should not be to "avoid arrow functions", but to avoid capturing the this context in a closure (which is usually done by using an arrow function).
A solution would be to bind the method to the component:
class MyClass extends React.Component {
constructor(props){
super(props);
this.addTodo = this.addTodo.bind(this); // <-- bind method to the components `this`
}
myValue = 'some value';
addTodo(){
console.log( 'ok, value is:', this.myValue );
}
render() {
return <>
<button onClick={ this.addTodo } > works without arrow function </button>
</>;
}
}
Remark:
I would recommend that you just should forget that you have ever read the misleading recommendation in that blog post, and find better sources of knowledge.
I think closures, the .bind(this) pattern and optimizing functional components are beyond the scope of this question topic.
If you still want to optimize your functional component, unrelated to that blog post, then I think you should ask a new question.

Binding this on method receiving parameters

I have an event handler which calls a fat arrow function to run a method.
import React, { Component } from 'react';
class App extends Component {
sayHi = msg => {
console.log(msg);
};
render() {
return (
<div>
<button onClick={() => this.sayHi('Hi')}>Console Hi!</button>
</div>
);
}
}
export default App;
I´m learning about contexts and bind() and, I want to convert this example to bind this. My problem is with the parameter that I´m passing when the fat arrow function executes the method, aka, 'Hi'
Is there a way to keep something like this...
<button onClick={this.sayHi('Hi')}>Console Hi!</button>
I tried different ways without good results. Mostly, focused on
constructor(props) {
super(props);
this.sayHi = this.sayHi.bind(this);
}
sayHi = () => {
console.log(msg);
};
And yes... I don´t want to move the 'Hi' to the method or constructor.
I´m trying to learn and understand. I will appreciate any kind of help or orientation.
You are mixing things. There are two cases for your situation and you are trying to use them both.
Binding to this
When do you need you bind your function to this? If you are calling your function in callback like your button (one of the cases of course) and you need to use this in this function then you need to bind it. If you don't use this then there is no need to bind it either.
sayHi() {
console.log("hi");
};
render() {
return (
<div>
<button onClick={this.sayHi}>Console Hi!</button>
</div>
);
}
}
Here, you don't need to bind it, also you can use the function with its reference since there is no argument.
constructor(props) {
super(props);
this.state = {
name: "foo",
}
this.sayHi = this.sayHi.bind(this);
}
sayHi() {
console.log(this.state.name);
};
render() {
return (
<div>
<button onClick={this.sayHi}>Console Hi!</button>
</div>
);
}
}
Here you are using this in the function, so you need to bind it in the constructor or define it as an arrow function.
Your situation
Now, your situation: You are defining your function as an arrow one, no need to bind it anymore if you will use this there. But you are not using it, then no need to use an arrow function. Also, you need to pass an argument to it. So, you need to find a way to accomplish this.
The first method, use an arrow function for onClick. Since if you don't use a callback here you can't use click.
sayHi(msg) {
console.log(msg);
};
render() {
return (
<div>
<button onClick={() => this.sayHi("hi")}>Console Hi!</button>
</div>
);
}
}
If you use like this.sayHi("hi") then this function is invoked in the first render, not with a click.
You can use .bind here as a second method also.
sayHi(msg) {
console.log(msg);
};
render() {
return (
<div>
<button onClick={this.sayHi.bind(null,"hi")}>Console Hi!</button>
</div>
);
}
}
See, we use bind but did not use this since we don't need it. We are not using this in our sayHi function.

React binding this to a class method

So i'm reading a book on React which said I have to bind my methods like
this.onClickMe = this.onClickMe.bind(this);
but it looks to work just fine without using the above code
class ExplainBindingsComponent extends Component {
onClickMe() {
console.log(this);
}
render() {
return (
<button
onClick={ () => { this.onClickMe() } }
type="button"
>
Click Me
</button>
);
}
}
but it's saying I should do something like this,
class ExplainBindingsComponent extends Component {
constructor() {
super();
this.onClickMe = this.onClickMe.bind(this);
}
onClickMe() {
console.log(this);
}
render() {
return (
<button
onClick={this.onClickMe}
type="button"
>
Click Me
</button>
);
}
}
is this.onClickMe = this.onClickMe.bind(this); still something I have to do? and if so what does it do vs my above example
There are multiple ways to bind your function to the lexical context of the React class,
one such method is to bind it in the constructor,
other method is to use class fields as arrow functions, and
the third way to bind in the render using .bind or arrow,
Each of these can be used, however its best to avoid binding in the render since a new function is returned on each render
Using class field as arrow function.
class ExplainBindingsComponent extends Component {
onClickMe = () => {
console.log(this);
}
render() {
return (
<button
onClick={ this.onClickMe }
type="button"
>
Click Me
</button>
);
}
}
Binding in render
onClick={() => this.onClickMe() }
or
onClick={this.onClick.bind(this)}
is this.onClickMe = this.onClickMe.bind(this); still something I have to do?
You don't have to do it if you use arrow functions that capture lexical this. But it is considered to be a best practice because it allows you to avoid function creation inside render.
render() {
return (
<button
/* creates new function on every render call*/
onClick={ () => { this.onClickMe() } }
type="button"
>
Click Me
</button>
);
}
vs
constructor() {
super();
// creates function once per component instance
this.onClickMe = this.onClickMe.bind(this);
}
In your case, you don't need to because you use arrow function where this is bound to a context in which arrow function is defined - in this case to your component.
this.onClickMe = this.onClickMe.bind(this)
it's necessary when you pass function without any binding so it might be invoked where this will point to another object.
For anyone who is following 'React Step by Step' link and got stuck because in the example Clock, they haven't written any bind and it has worked well, but on the next example Toggle, they started to use bind(this).
Well, you can see the function tick(){...} (Clock class) and handleClick (){...} (Toggle class) are similar, as they both use the word this inside. Well the difference between them is how they are called. In the first (Clock), it is called using arrow function (inside componentDidMount() method) and using it allows you to bind automatically the word this with the object. On the other hand, the second method is not using ()=>{}, and need to bind this with the object. So for this purpose the assignment this.handleClick = this.handleClick.bind(this); helps you.
There are 3 ways to bind this
While defining the state. Like this:
this.state = {
this.eventHandler = this.eventHandler.bind(this)
}
Change the normal function to arrow function. Like this:
eventHandler = () => {
console.log('event handler');
}
Pass arrow function directly into the props. Like this:
<input onClick={(e) => this.eventHandler(e) } />
Hope this could resolve your problem.
~RDaksh

Pass argument onClick to props.function() executed via refs

I'm sorry this title might be a bit of a head-scratcher, I just can't quite think of how exactly to phrase my issue (open to any suggestions).
Basically there are 3 components: parent, ChildA, and ChildB
Parent renders both children, ChildB has a ref tag, Parent uses that ref tag to pass ChildB's function to ChildA.
class Parent extends Component {
render() {
<ChildA openChildB={() => this.childb.open()} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
Pretty basic.
The issue I am running into is that when ChildA executes that function it needs to pass an argument. I can't seem to figure out the correct way to do that.
I tried using a different syntax to pass the function down to ChildA -
<ChildA openChildB={this.childb.open} />
but that results in an error, Can't Read Property Of Undefined.
How can I pass a variable through this function?
Any help would be very appreciated!
Edit: I know I could pass the argument up to Parent and then from there place it in the function () => this.childb.open(arg) but for the sake of organization I would really prefer to handle that all within ChildA.
You can use the following:
class Parent extends Component {
render() {
<ChildA openChildB={(arg1) => this.childb.open(arg1)} />
<ChildB ref={instance => { this.childb = instance; }} />
}
}
And inside ChildA you should make sure to pass the relevant argument when you call the this.props.openChildB:
function handleOpenOnB() {
// This is an example, you should use the relevant value you want to pass to the openChildB function
this.props.openChildB(this.state.val);
}

How to bind function in constructor that has a parameter in Reactjs

So here's my function:
remove(element)
{
this.setState({ search: this.state.search.filter( item => item !== element ) });
}
I get this error:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor).
Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
If I have it set up like this:
constructor()
{
this.remove = this.remove.bind(this);
}
render()
{
return (
<div>
{ this.state.search.map( (item, index) =>
(
<button key={index} onClick={this.remove(item)}>{item.search}: {item.text}</button>
))
}
</div>
</div>
);
}
But if works fine if I remove the binding (well doesn't really matter) from the constructor and change the button line to this:
<button key={index} onClick={this.remove.bind(this, item)}>{item.search}: {item.text}</button>
So my question is, is there a way to bind it in the constructor so that it can take on the parameter?
The difference between this.remove(item) and this.remove.bind(this, item) is that the first calls the function while the second creates a new function.
So my question is, is there a way to bind it in the constructor so that it can take on the parameter?
You can use this.remove.bind(this, item) and perform the binding the constructor, though it is unnecessary.
If you want to pass item to the event handler, then you have to create a new function in .map that can access item, with your current setup. This can be done via .bind or via a closure. In either case, binding in the constructor is simply not necessary.
You can only avoid creating a new function if provide item in a different way, e.g. wrapping the button with another component that takes item as a prop (therefore pushing the function creation further down):
function Button({item, onClick}) {
return <button onClick={() => onClick(item)}>{item.search}: {item.text}</button>;
}
class Component extends React.Component {
constructor()
{
this.remove = this.remove.bind(this);
}
render()
{
return (
<div>
{ this.state.search.map( (item, index) =>
(
<Button key={index} onClick={this.remove} item={item} />
))
}
</div>
</div>
);
}
}

Categories