Why is 'this' itself undefined in function scope - javascript

I have the following code:
class App extends Component {
constructor(){
super()
//this.buttonOneClick = this.buttonOneClick.bind(this);
}
buttonOneClick() {
const { setTestData } = this.props;
setTestData("test");
}
render() {
const { testData } = this.props;
return (
<div className="App">
<h1>Test App</h1>
<div>{testData}</div>
<button onClick={this.buttonOneClick}>Test</button>
</div>
);
}
}
The component App is exported with react-redux's connect method to map the setTestData-function to the props (thats where setTestData is coming from).
When I set the buttonOneClick() handler without biding the component context to it (see the outcommented line in my code in constructor()) I get the following error:
Cannot read property 'props' of undefined
I understand that props wouldn't be defined since the handler is executed in the global context which doesn't know about props but how I understand the error it implies that this itself is not defined which doesn't make sense to me. Why is this the case? Shouldn't this always be defined in any execution context?
Thanks in advance

Shouldn't this always be defined in any execution context?
No.
There is no implicit this value in strict mode.
"use strict";
function example() {
alert(this);
}
example();

We’re getting an error because this is not defined when onClick calls our buttonOneClick() function.
Usually, you would fix this by binding this to the buttonOneClick function so it always stays the same. So we have to bind that in the constructor.
this.buttonOneClick = this.buttonOneClick.bind(this);
Possible work-around if you don't want to use the binding and all is to use the es2015 javascript arrow functions
buttonOneClick = () => console.log(this.props);
An arrow function does not have its own this so it will use this off its context, which is our Component. This is called Lexical Scoping

Related

Accessing props in separate function in a functional component

I have a simple Parent Functional Component (App.js) and a child Functional component (counters.jsx), I want to pass a signal/prop from child to parent. I want to send the props as an argument to another function in my child component. I can easily trigger parent function by onClick={props.parentCallback}. but if I send the same props as an argument to another function in child component, I get the error "Expected an assignment or function call and instead saw an expression no-unused-expressions"
//This is parent Component, App.js
function App() {
return (
<div className="App">
<Counters parentCallback = {handleCallback}/>
</div>
);
}
const handleCallback = () => {
console.log("CALLED FRM CHILD");
}
//This is Child Component- Counters.jsx
function Counters (props) {
return (
<div>
<button onClick={ ()=> parentHandler(props) }>CALL PARENT</button><hr/>
{//onClick={props.parentCallback} - This works well}
</div>
);
};
function parentHandler (props) {
props.parentCallback; // This line throws the aforementioned error
}
You are just referencing the function. Not calling it. You can modify parentHandler function as
function parentHandler (props) {
props.parentCallback()
}
no-unused-expressions usually means that you evaluated a value but never used. This rule aims to eliminate unused expressions.
For example if(true) 0
In your case you are calling props.parentCallback without parenthesis, leaving unused. Either you add parenthesis after your function call like this props.parentCallback() or return some value at the end. By that your eslint error will disappear.
Also onClick={props.parentCallback} - This works well because you are just passing a reference of your function to the parent Component. But here onClick={ ()=> parentHandler(props) } you are creating a new inline function on each render.
Thank u for ur answer guys, but none of the answer is exactly what I wanted. Finally, I got the answer myself as it is:
Do you need the function to run now?
Then add the () to execute it
Do you need to function to be referenced so it is called later?
Do not add the ().
More example:
onClick=parentCallback() means call "parentCallback() ASAP, and set its return value to onClick".
On the other hand, onClick=parentCallback means "onClick is an alias for parentCallback. If you call onClick(), you get the same results as calling parentCallback()"
I found it by googling "function call and function reference".

How to bind a function of a React component to "this" without a constructor?

As many know, this.someFunction = this.someFunction.bind(this) can be used in React class components. However, sometimes it's burdensome to create classes for very simple components. So, how can I bind a function to this without creating a class?
use arrow function arrow function
In React 16.8, you can use hooks for stateful components, essentially allowing you to write everything as a function and eliminating the need to use classes and all the caveats that come with them (this, .bind() etc).
Usage example
Here's an example of using the useState() hook and the useEffect() hook inside a function, along with an arrow function, which is already bound to the component's context:
import React, { useState, useEffect } from 'react';
function LimitedTextarea({ rows, cols, value, limit }) {
const [content, setContent] = useState(value);
const setFormattedContent = text => {
text.length > limit ? setContent(text.slice(0, limit)) : setContent(text);
};
useEffect(() => {
setFormattedContent(content);
}, []);
return (
<div>
<textarea
rows={rows}
cols={cols}
onChange={event => setFormattedContent(event.target.value)}
value={content}
/>
<p>
{content.length}/{limit}
</p>
</div>
);
}
Short explanation
You use useState() to create a state variable content and a method to update that variable, setContent().
You create an arrow function setFormattedContent to update the content state variable via the setContent method, which is already bound to context.
You use useEffect() to call setFormattedContent on the value of the content state variable.
Finally, you return whatever you would have in your render() method in a class component.
this makes sense in class component because it refers to component instance. It doesn't make sense in functional component because it is either undefined or a global, depending on the environment and how a function was declared.
As for class components, explicit constructor can be omitted if it's not needed, class fields can be used to assign instance properties, including bound methods:
class Foo extends Component {
foo = this.foo.bind(this);
foo() { ... }
...
}
Which is syntactic sugar for:
class Foo extends Component {
constructor(props) {
super(props);
this.foo = this.foo.bind(this);
}
foo() { ... }
...
}
Bound prototype methods have several benefits over instance arrow methods.
Yes arrow function is the solution.
Bind with this,
this.someFunction = this.someFunction.bind(this)
Using arrow function,
someFunction = () => {
// do something
}
There is no need to bind "this" If you use arrow function. It has simple and cleaner syntax.
someFuncton = () => {
// whatever write here...
}

React : Call a function from inside render function

I have referred to couple of similar questions, but I have a situation little different.
Call a React Function from inside Render
How to Call a Function inside a Render in React/Jsx
React: Cant call a function inside child component
export default class CodeEditor extends React.Component {
appendAssets(asset) {
console.log(asset)
this.refs.editor.editor.insert(`player.asset('${asset}')`)
this.refs.editor.editor.focus()
}
render() {
function sequenceHierarchy (data, outputArray) {
level++
data.forEach(function (asset){
outputArray.push(<li className={`level_${level}`}><button onClick={_ => this.appendAssets(asset.name)}>{asset.name}</button></li>)
if(asset.children.length) {
sequenceHierarchy(asset.children, outputArray)
}
})
level--
}
}
}
So the onClick of button inside sequenceHierarchy function has to call appendAssets. Of course since this wouldn't be able to call it as it is not the part of this component, I also tried with just appendAssets(asset.name), still it gives a error of Uncaught ReferenceError: appendAssets is not defined
I strongly advise you to read this answer for understanding this keyword and its behaviors inside function.
Quoting from the linked answer:
this (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scope, like other variables.
(Please read whole linked answer before proceeding further)
You can either set a reference to the variable which refers to correct this. See this JSFiddle
let that = this; // assign this reference to variable that.
function sequenceHierarchy (data, outputArray) {
data.forEach((item) => {
outputArray.push(
<li>
<button onClick={that.appendAssets.bind(this, item)}>
Append {item}
</button>
</li>
);
})
}
or
You can use bind method to set correct this, while invoking sequenceHierarchy function as follows. See this JSFiddle
{sequenceHierarchy.bind(this, (['1', '2', '3'], []))}
or
You can use ES6 arrow function to invoke sequenceHierarchy function like this.
{() => {sequenceHierarchy(['1', '2', '3'], [])}}
All above methods will work fine.
Hope this helps :)

ES6 method is not defined in create-react-app

I'm having difficulty understanding why create-react-app is unable to compile, telling me that error 'updateWord' is not defined no-undef. I'm fairly new to React with ES6. Normally I would write a component like const App = React.createClass({ }); but I've decided to try out some syntactical sugar instead.
I have parent component App and a child component Input:
class App extends Component {
constructor(props) {
super(props);
// all other code omitted...
}
handleInput(input) {
console.log(`handle: ${input}`);
updateWord(input);
// this also causes an error
// this.updateWord(input);
}
updateWord(input) {
console.log(`update ${input}`);
// why isn't this defined?
}
render() {
return (
<div className="App">
<Input onInput={this.handleInput} />
</div>
);
}
}
class Input extends Component {
handleInput(e) {
let input = e.target.value;
this.props.onInput(input);
}
render() {
return (
<form>
<input onChange={this.handleInput.bind(this)} />
</form>
);
}
}
I've tried changing to this.updateWord(input); instead of updateWord(input) but to no avail. I get:
"App.js:55 Uncaught TypeError: this.updateWord is not a function"
Normally when I implement a similar pattern (with ES5) to what I'm doing now I have no difficulties. For example:
const App = React.createClass({
getInitialState: function() {
// all other code omitted...
},
handleInput: function(input) {
console.log(`handle: ${input}`);
this.updateWord(input);
},
updateWord: function(input) {
console.log(`update ${input}`);
// In theory, this implementation would not cause any errors?
},
render: function() {
return (
<div className="App">
<Input onInput={this.handleInput} />
</div>
);
}
}
The problem is that when you do this.updateWord(...) in this.handleInput, this refers to the Input component. Let me illustrate the problem:
When you set the onInput handler, like so:
onInput={this.handleInput}
Here, since your Input component is calling the function, this refers to the Input component. This is due to the line:
this.props.onInput(input);
The Input component is calling handleInput. That means, in your handleInput function, the this context is Input. Consider the line:
this.updateWord(input);
in the handleInput function. Here you call this.updateWord, but since this is Input, it tries to call updateWord from Input which does not exist, thus throwing the error.
The solution is to explicitly bind the this context as the class (App component) instead of the Input component, using either Function.prototype.bind or an arrow function. From the documentation:
The bind() method creates a new function that, when called, has its this keyword set to the provided value
You can apply it like so:
onInput={this.handleInput.bind(this)}
Or more preferably in the constructor:
this.handleInput = this.handleInput.bind(this);
With the second option you may then do:
onInput={this.handleInput}
(This is more preferable as binding in the render method will create a new function every time on render, which isn't preferred).
The this context in the line above is the class. Since you bind this, the class will be correctly used as this context in the function and executing this.updateWord will invoke the method in the class.
An even more preferable way is to use arrow functions instead of regular ES6 methods. From the documentation:
An arrow function expression has a shorter syntax compared to function expressions and does not bind its own this, arguments, super, or new.target.
We can apply this by assigning handleInput to an arrow function instead of a regular method:
handleInput = (input) => {
console.log(`handle: ${input}`);
this.updateWord(input);
}
This will eliminate the use of bind completely and instead, use arrow functions. Since arrow functions don't bind their own this, it means this refers to the enclosing context. In the example above, a method is not used thus this refers to the class (the enclosing context). That will correctly call the class method updateWord, and consequently, you need not change the onInput event handler if you go this route.

Render refactoring leads to undefined function

I have a React class that functions correctly when written as such:
const LoginContainer = React.createClass({
authenticate: () => {
console.log('TODO: Finish authenticating');
},
render: function() {
return (
<Login authenticate={this.authenticate} />
);
}
});
To conform to the style guide we are using, I should use the arrow shorthand for render:
render: () =>
(
<Login authenticate={ this.authenticate } />
),
However, as soon as I rewrite this, I get
Uncaught TypeError: Cannot read property 'authenticate' of undefined
How can I get a reference to authenticate within my arrow function?
Please note that I understand the value of this is scoped differently in an arrow function, but what I'm trying to figure out is how to get the binding correct within a React class. This may be more of a React question than vanilla JS.
Arrow functions lexically binds context so that this refers to the context of the outer scope.

Categories