'this' is undefined in React component function - javascript

When I try to execute the below code, the value of this within the function renderContents is undefined.
I had thought that fat arrow functions bound this automatically. Is this not the case? If not, how can I ensure that this is passed to the function renderContents?
Code sample:
class Box extends React.Component {
renderContents = () => {
console.log(this); // undefined
return (
<div></div>
)
}
render() {
const {
someValue,
} = this.props;
return (
<div>
{someValue ? this.renderContents() : null}
</div>
);
}
}

There is nothing wrong. I just called it like this:
ReactDOM.render((<Box someValue={true} />), document.getElementById('content'));
and I got the value of this:
So I think the problem would be, you aren't calling it right, are you setting someValue??

You make mistake in renderContent method. You declare it as function and it have no context of your component.
Please try to declare methods like this:
renderContent() {
...your code...
}
Instead this:
renderContent = () => {}

Have you tried to add an constructor method?
constructor(props) {
super(props)
}
EDIT:
Instead of using arrow functions, you could bind the context in constructor like this:
class Box extends React.Component {
constructor(props) {
super(props)
this.renderContents = this.renderContents.bind(this)
}
renderContents() {
console.log(this);
return (
<div></div>
)
}
render() {
const {
someValue,
} = this.props;
return (
<div>
{someValue ? this.renderContents() : null}
</div>
);
}
}
In react classes just some methods are implicitly binded (render, componentDidMount, componentWillUnmount, etc.), other methods you have to bind manually in constructor or use arrow functions.

Related

ReactJS onClick event cant call its function

I have problem here with this.greet() is undefined i have to bind onlick event dirrently to work, any ideas how to fix it ?
import React from 'react';
export default class Example1 extends React.Component {
constructor() {
super();
this.message = {
greeting: 'Hello',
name: 'World',
};
}
greet() {
const { greeting, name } = this.message;
console.log(`${greeting} ${name}!`);
}
onClick() {
try {
this.greet();
} catch (e) {
console.error('Why have I failed? Can you fix me?');
}
}
render() {
return (
<div>
<button onClick={this.onClick}>Greet the world</button>
</div>
);
}
}
i have tried many other ways but it didnt work out anyway.
You lost this reference, you have to bind your functions:
<button onClick={this.onClick.bind(this)}>Greet the world</button>
or pass an arrow function
<button onClick={() => this.onClick()}>Greet the world</button>
The reason you are getting undefined as a value for this.greet() is because the this keyword in JS references the current object and in this case it doesn't exist and so .greet() method is actually attached to the window object. The solution you used as you described with binding works because bind attached the method to the current object context. Now, if you don't want to bind methods, you can use arrow functions like so:
import React from 'react';
export default class Example1 extends React.Component {
constructor() {
super();
this.message = {
greeting: 'Hello',
name: 'World',
};
}
greet = () => {
const { greeting, name } = this.message;
console.log(`${greeting} ${name}!`);
}
onClick = () => {
try {
this.greet();
} catch (e) {
console.error('Why have I failed? Can you fix me?');
}
}
render() {
return (
<div>
<button onClick={this.onClick}>Greet the world</button>
</div>
);
}
}
This works because the value of this is inherited into the local scope of the methods of the component class.
if you want have "this" in your class component you should add props inside constructor invocation and also into the super
constructor(props) {
super(props);
this.greet = this.greet.bind(this) // need's to be bounded
}
Problem is this keyword.
This is because of the way this works in javascript.
this loses it's context when it gets used in callbacks.
There are two solutions for this problem. But I would recommend the arrow function solution the most.
Bind this in the onClick callback.
onClick() {
try {
this.greet();
} catch (e) {
console.error('Why have I failed? Can you fix me?');
}
}
render() {
return (
<div>
<button onClick={this.onClick.bind(this)}>Greet the world</button>
</div>
);
}
Use arrow functions for defining callbacks, ( personally recommended )
onClick = () => {
try {
this.greet();
} catch (e) {
console.error('Why have I failed? Can you fix me?');
}
}
render() {
return (
<div>
<button onClick={this.onClick}>Greet the world</button>
</div>
);
}
First your function signature should be descriptive and then you can call the function like this
<button onClick={this.onClick()}>Greet the world</button>

Why was "this" lost? [duplicate]

class PlayerControls extends React.Component {
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
}
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={this.onToggleLoop}
spin={this.state.loopActive}
/>
<FontAwesome
className={shuffleClassName}
name='random'
onClick={this.onToggleShuffle}
/>
</div>
);
}
onToggleLoop(event) {
// "this is undefined??" <--- here
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
I want to update loopActive state on toggle, but this object is undefined in the handler. According to the tutorial doc, I this should refer to the component. Am I missing something?
ES6 React.Component doesn't auto bind methods to itself. You need to bind them yourself in constructor. Like this:
constructor (props){
super(props);
this.state = {
loopActive: false,
shuffleActive: false,
};
this.onToggleLoop = this.onToggleLoop.bind(this);
}
There are a couple of ways.
One is to add
this.onToggleLoop = this.onToggleLoop.bind(this); in the constructor.
Another is arrow functions
onToggleLoop = (event) => {...}.
And then there is onClick={this.onToggleLoop.bind(this)}.
Write your function this way:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
Fat Arrow Functions
the binding for the keyword this is the same outside and inside the fat arrow function. This is different than functions declared with function, which can bind this to another object upon invocation. Maintaining the this binding is very convenient for operations like mapping: this.items.map(x => this.doSomethingWith(x)).
I ran into a similar bind in a render function and ended up passing the context of this in the following way:
{someList.map(function(listItem) {
// your code
}, this)}
I've also used:
{someList.map((listItem, index) =>
<div onClick={this.someFunction.bind(this, listItem)} />
)}
in my case this was the solution = () => {}
methodName = (params) => {
//your code here with this.something
}
You should notice that this depends on how function is invoked
ie: when a function is called as a method of an object, its this is set to the object the method is called on.
this is accessible in JSX context as your component object, so you can call your desired method inline as this method.
If you just pass reference to function/method, it seems that react will invoke it as independent function.
onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined
onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object
If you are using babel, you bind 'this' using ES7 bind operator
https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding
export default class SignupPage extends React.Component {
constructor(props) {
super(props);
}
handleSubmit(e) {
e.preventDefault();
const data = {
email: this.refs.email.value,
}
}
render() {
const {errors} = this.props;
return (
<div className="view-container registrations new">
<main>
<form id="sign_up_form" onSubmit={::this.handleSubmit}>
<div className="field">
<input ref="email" id="user_email" type="email" placeholder="Email" />
</div>
<div className="field">
<input ref="password" id="user_password" type="new-password" placeholder="Password" />
</div>
<button type="submit">Sign up</button>
</form>
</main>
</div>
)
}
}
I want to give an explanation of why this is undefined:
If we use this in a function that is not an arrow function, this is bound to a global object when not in strict mode. But with strict mode, this will be undefined (https://www.w3schools.com/js/js_this.asp).
And ES6 modules are always in strict mode (javascript: use strict is unnecessary inside of modules).
You can bind this in onToggleLoop function with the instance of PlayerControls component by using bind method inside the constructor:
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
this.onToggleLoop = this.onToggleLoop.bind(this)
}
Or use the arrow function instead:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
The arrow function does not have context, so this in the arrow function will represent the object that defined the arrow function.
If you call your created method in the lifecycle methods like componentDidMount... then you can only use the this.onToggleLoop = this.onToogleLoop.bind(this) and the fat arrow function onToggleLoop = (event) => {...}.
The normal approach of the declaration of a function in the constructor wont work because the lifecycle methods are called earlier.
In my case, for a stateless component that received the ref with forwardRef, I had to do what it is said here https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd
From this (onClick doesn't have access to the equivalent of 'this')
const Com = forwardRef((props, ref) => {
return <input ref={ref} onClick={() => {console.log(ref.current} } />
})
To this (it works)
const useCombinedRefs = (...refs) => {
const targetRef = React.useRef()
useEffect(() => {
refs.forEach(ref => {
if (!ref) return
if (typeof ref === 'function') ref(targetRef.current)
else ref.current = targetRef.current
})
}, [refs])
return targetRef
}
const Com = forwardRef((props, ref) => {
const innerRef = useRef()
const combinedRef = useCombinedRefs(ref, innerRef)
return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})
You can rewrite how your onToggleLoop method is called from your render() method.
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={(event) => this.onToggleLoop(event)}
spin={this.state.loopActive}
/>
</div>
);
}
The React documentation shows this pattern in making calls to functions from expressions in attributes.

How to use props and state inside a function declaration in a class component? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 2 years ago.
I have a class component which has a function inside it named printData() I want to make use of the states and props variable of the class inside this function. How can I do it?
Code -
class ReadData extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
}
}
componentDidMount() {
this.setState({ data: "Hello World" })
}
printData(props) {
console.log(this.props.data)
}
render() {
return (
<div>
<p>{this.state.data}</p>
<button onClick={this.printData}>Print</button>
</div>
)
}
}
The actual code is different. This is an example of what I am trying to implement. The console says
Uncaught (in promise) TypeError: Cannot read property 'props' of undefined
Using this.state.data instead of this.props.data also did not work.
Note: The render return successfully prints the desired output on the
window. Also, I want to make use of Class component and not functional component
You need to either bind the function or use arrow functions () => {} for this to work.
See doc: https://reactjs.org/docs/handling-events.html
class ReadData extends Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.printData = this.printData.bind(this);
}
printData() {
console.log(this.props.data)
}
render() {
return (
<div>
<p>{this.state.data}</p>
<button onClick={this.printData}>Print</button>
</div>
)
}
}
or
class ReadData extends Component {
// Use arrow function to make `this` work in the callback
printData = () => {
console.log(this.props.data)
}
render() {
return (
<div>
<p>{this.state.data}</p>
<button onClick={this.printData}>Print</button>
</div>
)
}
}

ReactJS - MouseClick gets triggered without a click

I'm new to React.JS and trying to create a click event on an element inside a rendered component.
Here is my code:
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick(i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};
My problem is that "handleClick" gets triggered after page load without any mouse clicked!
I've read about React.JS lifecycle and thought about registering to click event in componentDidMount method, but i'm really not sure about it:
Is there any easier way ? (or: Am I doing something wrong that triggers click ?)
If adding componentDidMount method is the right way - how can I get the element I create in render method ?
You should not use .bind when passing the callback as a prop. There’s a ESLint rule for that. You can read more about how to pass callback without breaking React performance here.
Summary:
make sure you aren’t calling functions but pass functions as handlers in your props.
make sure you do not create functions on every render, for that, you need to bind your handlers in parent component, pass correct the required data (such as indices of iteration) down the child component and have it call the parent’s handler with the data it has
Ideally you’d create another component for the rows and pass the callback there. Moreover, ideally you’d bind the onClick in the parent component’s constructor (or componentWillMount). Otherwise every time render runs a new function is created (in both anonymous function handler () => { this.onClick() } and this.onClick.bind and defeat React’s vdom diff causing every row to rerender every time.
So:
class InputPanel extends React.Component{
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{board.map((row, i) => <div>
{row.map((cell, j) => <Digit
onClick={this.handleClick})
i={i}
j={j}
cell={cell}
/>)}
</div>)}
</div>
);
}
};
class Digit extends React.Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onClick(this.props.i, this.props.j);
}
render() {
return <div
className="digit"
onClick={this.handleClick}
>{this.props.cell}</div>
}
}
It is because you are calling this.handleClick() function instead of providing a function definition as onClick prop.
Try changing the div line like this:
<div className="digit" onClick={ () => this.handleClick(i,j) }>{cell}</div>
Also you have to bind this.handleClick() function. You can add a constructor and bind all the member functions of a class there. that's the best practice in ES6.
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
You call this function in render. You should only transfer function and bind params.
onClick={this.handleClick.bind(null,i,j)}
You should use .bind().
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick.bind(null,i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};

How to access component methods from “outside” in ReactJS?

Why can’t I access the component methods from “outside” in ReactJS? Why is it not possible and is there any way to solve it?
Consider the code:
var Parent = React.createClass({
render: function() {
var child = <Child />;
return (
<div>
{child.someMethod()} // expect "bar", got a "not a function" error.
</div>
);
}
});
var Child = React.createClass({
render: function() {
return (
<div>
foo
</div>
);
},
someMethod: function() {
return 'bar';
}
});
React.renderComponent(<Parent />, document.body);
React provides an interface for what you are trying to do via the ref attribute. Assign a component a ref, and its current attribute will be your custom component:
class Parent extends React.Class {
constructor(props) {
this._child = React.createRef();
}
componentDidMount() {
console.log(this._child.current.someMethod()); // Prints 'bar'
}
render() {
return (
<div>
<Child ref={this._child} />
</div>
);
}
}
Note: This will only work if the child component is declared as a class, as per documentation found here: https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a-ref-to-a-class-component
Update 2019-04-01: Changed example to use a class and createRef per latest React docs.
Update 2016-09-19: Changed example to use ref callback per guidance from the ref String attribute docs.
If you want to call functions on components from outside React, you can call them on the return value of renderComponent:
var Child = React.createClass({…});
var myChild = React.renderComponent(Child);
myChild.someMethod();
The only way to get a handle to a React Component instance outside of React is by storing the return value of React.renderComponent. Source.
Alternatively, if the method on Child is truly static (not a product of current props, state) you can define it on statics and then access it as you would a static class method. For example:
var Child = React.createClass({
statics: {
someMethod: function() {
return 'bar';
}
},
// ...
});
console.log(Child.someMethod()) // bar
As of React 16.3 React.createRef can be used, (use ref.current to access)
var ref = React.createRef()
var parent = (
<div>
<Child ref={ref} />
<button onClick={e=>console.log(ref.current)}
</div>
);
React.renderComponent(parent, document.body)
Since React 0.12 the API is slightly changed. The valid code to initialize myChild would be the following:
var Child = React.createClass({…});
var myChild = React.render(React.createElement(Child, {}), mountNode);
myChild.someMethod();
You could also do it like this, not sure if it's a good plan :D
class Parent extends Component {
handleClick() {
if (this._getAlert !== null) {
this._getAlert()
}
}
render() {
return (
<div>
<Child>
{(getAlert, childScope) => (
<span> {!this._getAlert ? this._getAlert = getAlert.bind(childScope) : null}</span>
)}
</Child>
<button onClick={() => this.handleClick()}> Click me</button>
</div>
);
}
}
class Child extends Component {
constructor() {
super();
this.state = { count: 0 }
}
getAlert() {
alert(`Child function called state: ${this.state.count}`);
this.setState({ count: this.state.count + 1 });
}
render() {
return this.props.children(this.getAlert, this)
}
}
As mentioned in some of the comments, ReactDOM.render no longer returns the component instance. You can pass a ref callback in when rendering the root of the component to get the instance, like so:
// React code (jsx)
function MyWidget(el, refCb) {
ReactDOM.render(<MyComponent ref={refCb} />, el);
}
export default MyWidget;
and:
// vanilla javascript code
var global_widget_instance;
MyApp.MyWidget(document.getElementById('my_container'), function(widget) {
global_widget_instance = widget;
});
global_widget_instance.myCoolMethod();
Another way so easy:
function outside:
function funx(functionEvents, params) {
console.log("events of funx function: ", functionEvents);
console.log("this of component: ", this);
console.log("params: ", params);
thisFunction.persist();
}
Bind it:
constructor(props) {
super(props);
this.state = {};
this.funxBinded = funx.bind(this);
}
}
Please see complete tutorial here: How to use "this" of a React Component from outside?

Categories

Resources