React functions and events - javascript

I am fairly new to react, I was looking at code and I ran into an event
code inside of a class that looked like
zipValueChanged(event) {
const zip = event.target.value;
this.setState({
zipValue: zip,
})
I was wondering why the event function does not have the function keyword anywhere, I did see a binding in the constructor ( this.zipValueChanged = this.zipValueChanged.bind(this);) However, I do not understand why this works.
Thank you so much!

This (this.zipValueChanged = this.zipValueChanged.bind(this);) binding in constructor was done so that any method defined in the class can work.
You need to bind your method in your constructor in order to work it properly -
this.yourFunction = this.yourFunction.bind(this);

This is the new syntax for Class methods definition. You can check out documentation here.
From now on inside a class you can define methods this way :
class A{
render() {
//your code here
}
}

Related

Why do we need to use bind() in ReactJS to access this.props or this.state? [duplicate]

This question already has answers here:
Why and when do we need to bind functions and eventHandlers in React?
(2 answers)
Closed 3 years ago.
look at this code for example
import React, { Component } from ‘react’;
class App extends Component {
constructor(props) {
super(props);
this.clickFunction = this.clickFunction.bind(this);
}
clickFunction() {
console.log(this.props.value);
}
render() {
return(
<div onClick={this.clickFunction}>Click Me!</div>
);
}
}
what's the purpose of bind(this) ? it binds the function clickFunction to the context of the object which clickFunction is already bound to , let me illustrate what i am trying to say with a normal javascript code :
class my_class {
constructor() {
this.run = this.run.bind(this)
}
run() {
console.log(this.data)
}
}
my_class.data = 'this is data'
new my_class().run() //outputs 'undefined'
and if you remove bind(this) it will give you the same results
constructor() {
this.run = this.run
}
results :
new my_class().run() //still outputs 'undefined'
i am sure i am understanding something wrong and this might the worst question on earth however i am new to es6 and i am not used to classes yet so i apologize for that
Blame JavaScript not React. This is done to retain object instance when the function is going to be passed. Certainly, it must be semantically correct for the function to expect such object. Most common case is to bind this when passing object method. The keyword This depends on how the function is called not how/where it is created. The relationship to This should be maintained at invocation.
Consider:
class Welcome extends React.Component {
render() {
return <button onClick={this.sayName}>Say My
Name</button>;
}
sayName() {
alert(this.props.name);
}
}
In React, you invoke like this: . This renders a button. Clicking the button should trigger an alert with "Bob".
Except it doesn't. Because in the above example, this would be undefined in the sayName function.
What's happening inside the render function is that this refers to the current instance of our React component. That component has a sayName function defined, so this.sayName points to our function, just fine and dandy.
But what React is doing behind the scenes is assigning this.sayName to another variable. That is, it's just like this:
let onClick = this.sayName;
onClick(); // Technically a click event is passed
to onClick
// but this doesn't matter for our purposes
We get an error. Because this is undefined. This is
extra confusing because in previous versions of React, React would "autobind" the event handler for you, so it would work. But at some point, Facebook decided to stop doing that, so ... here we are.
So how can we fix our component? We just do binding ourselves, like this:
<button onClick={this.sayName.bind(this)}>Say My
Name</button>;
Or with ES6 syntax:
<button onClick={() => this.sayName()}>Say My
Name</button>;
And it should work!

Should I be binding also inbuilt React methods?

It is a common practice to be bind a user-created method inside a React.Component class.
class App extends React.Component { // simplified example, not actual code
constructor(){
//...
this.logIn=this.logIn.bind(this) // Binding of a method
}
}
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
What's however unclear to me, least from the documentation and so on I viewed, if we use in-built life-cycle methods such as render() or componentDidMount(), majority of the code snippets and also the official documentation seem to not explicitly bind to this
class App extends React.Component {
constructor(){
//....
this.componentDidMount = this.componentDidMount.bind(this)
// is there reason why we don't do this ^ ??
}
}
Is there some in-built binding already inside the React.Component we extend?
Or why don't we need to explicitly bind the life-cycle methods (componentDidMount()) like the rest of our created methods (logIn())?
I've made a component with the following:
...
componentDidMount() {
var that = this;
var x = 0;
}
...
render() {
....
<button onClick={this.componentDidMount}>DID MOUNT</button>
....
}
And the results are in -- when the function does initially mount, that is properly bound, but when clicked from the button, it is not.
Which means that the componentDidMount is not already bound, but it is called from the React internals with the proper context so that it doesn't need to be bound.
-- edit
Perhaps also of note: it's worth checking if you use an autobind package, if that binds the lifecycle methods. autobind-decorator in fact does!
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
You can also use arrow functions to been able to use this without binding:
sayHello=()=>{
return 'hello';
}
myOtherFunction=()=>{
console.log('I can acces the other function! Say:'+ this.sayHello())
}
And you don't need to bind the life-cycle methods
Edit: As the documentation says in https://reactjs.org/docs/handling-events.html
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.
So it is supposed that lifecycle methods are bound by default.

Class properties for react lifecycle methods

Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.

ReactJS call class method in Button after map()

I have 2 JS classes: App.jsx, Note.jsx.
Note.jsx:
export default class Note{
constructor(text) {
this.text = text;
}
changeNoteStatus(){
console.log(this.text);
}
}
In App.jsx:
let tmpNote = new Note(String(this.state.value));
this.notes.push({key:this.currId, value:tmpNote});
and in App.jsx i can call the changeNoteStatus() on a simple object but after creating a list of Note and using map() function:
return this.notes.map((obj) =>
<div className="note" onClick={obj.value.changeNoteStatus} key={obj.key} id={"note_" + obj.key}/>
)
The method doesn't recognize class field anymore (like this.text).
I have tried {this.notes[obj.key].changeNoteStatu} in mapping function too.
Please help me.
When you use it in an onClick like that, the handler will get called with the wrong this value.
One solution is to use Function#bind:
onClick={obj.value.changeNoteStatus.bind(obj.value)}
// --------------------------------^^^^^^^^^^^^^^^^
...but depending on how your overall code is structured, there may be more concise or efficient ways of doing it.

How to access an object member from event callback function in a class object on Ecmascript 6 (ES6)

I've got this :
class Register {
render(){
return (<div onchange={this.aFunction}></div>)
};
aFunction(event){
this.printSomething(); //Uncaught TypeError: undefined is not a function
}
printSomething(){
console.log('Horray');
}
}
How can I call printSomething from within aFunction? Es6 really confuses me.
Thank you.
You'll notice that when using ES6 classes with React a lot has changed.
Also, ES6 classes don't autobind the way the ES5 React.createClass would.
As a result, you need to properly bind the this of the function
You have two options
1. use an arrow function
render(){
return <div onchange={event => this.aFunction(event)}></div>;
}
2. use a binding
render(){
return <div onchange={this.aFunction.bind(this)}></div>;
}
I assume you're using React.js for this. If you are, you need to update
class Register
to
class Register extends React.Component
I think the problem is that this on render is bound on the div.
var foo = new Register;
var div = foo.render();
div.onchange(); // this is div, not foo
foo.aFunction(); // doesn't throw TypeError.

Categories

Resources