Still trying to learn React. I'm trying to show an image when you hover. This is my Item component.
import React from 'react';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
this.state = {
hover: false
};
}
mouseOver() {
this.setState({hover: true});
}
mouseOut() {
this.setState({hover: false});
}
render() {
const { item, i } = this.props;
return (
<div className="grid-box" onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>
{this.state.hover ? (<img src={Eyecon}/>) : null}
</div>
)
}
}
export default Item;
How would I make it so only the item I hover over shows the image?
This is just a 'this' binding issue. Put a console.log inside of your mouseOver and mouseOut methods and you'll notice that your state isn't changing.
There are many ways to bind the 'this' context in your class methods. I'll show you three ways to do it in this example (DO NOT do all three methods, just choose one).
import React from 'react';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
// 1. bind your functions in the constructor.
this.mouseOver = this.mouseOver.bind(this);
this.mouseOut = this.mouseOut.bind(this);
this.state = {
hover: false
};
}
// 2. bind it with fat arrows.
mouseOver = () => {
this.setState({hover: true});
}
mouseOut() {
this.setState({hover: false});
}
render() {
const { item, i } = this.props;
// 3. bind them in the render method (not recommended for performance reasons)
return (
<div className="grid-box" onMouseOver={this.mouseOver.bind(this)} onMouseOut={this.mouseOut.bind(this)}>
{this.state.hover ? (<img src={Eyecon}/>) : null}
</div>
)
}
}
export default Item;
Here's an explanation of different ways to bind your 'this' context in react using ES6 classes:
http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html
Maybe it's because you have to bind mouseOver and mouseOut calls in order to use this.setState inside them.
Replace:
<div className="grid-box" onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>
with:
<div className="grid-box" onMouseOver={this.mouseOver.bind(this)} onMouseOut={this.mouseOut.bind(this)}>
The other solutions suggested are perfectly valid, however you can solve this easily by just converting your functions to ES6 arrow functions.
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous.
Like so:
mouseOver = () => {
this.setState({hover: true});
}
mouseOut = () => {
this.setState({hover: false});
}
Simple.
Related
Im doing ReactJS course in Codeacademny and they confused me.
(EDIT - full code) Photo of the code :
and there's no constructor or anywhere call to any bind method for the scream class method.
However in further exercises they tell you can't do that.
I probably miss something.
Apparently this.scream is an arrow function. Arrow function does not require binding. It points to the right context by default.
scream = () => { ... }
and there's no constructor or anywhere call to any bind method for the scream class method.
You only have to bind this to the component instance when the method actually uses this internally.
That's not the case in your example, so there is no need to bind it. No matter how the method is executed, it will always produce the same output.
Here is an example without React to demonstrate the difference:
var obj = {
value: 42,
method1() { // doesn't use `this`
console.log("yey!");
},
method2() { // uses `this`
console.log(this.value);
},
};
obj.method1(); // works
obj.method2(); // works
var m1 = obj.method1;
var m2 = obj.method2;
m1(); // works
m2(); // BROKEN!
var m2bound = obj.method2.bind(obj);
m2bound(); // works
scream = () => { ... }
render() {
return <button onClick={()=>this.scream()}>AAAAAH!</button>;
}
ou 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.
When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. For example, this Toggle component renders a button that lets the user toggle between “ON” and “OFF” states:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);```
You can simply use an arrow function (no need to bind in constructor).
scream = () => { console.log('Here') }
render() {
return <button onClick={this.scream}>AAAAAH!</button>;
}
Or you can call this function inline by.
render() {
return <button onClick={() => console.log('Here')}>AAAAAH!</button>;
}
You should use arrow functions for event handling to bind the function to the object. Other solution is to auto bind each function in the constructor like :
class Test{
constructor(){
Object.getOwnPropertyNames(Test.prototype).forEach(
method => this[method] = this[method].bind(this));
}
Read about #AutoBind decorator for more details.
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.
I have a React component and I want to toggle a css class when clicked.
So I have this:
export class myComponent extends React.Component {
constructor() {
super();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.clicked}><span ref="btn" className="glyphicon"> </span></div>
</div>
);
}
handleClick() {
this.refs.btn.classList.toggle('active');
}
componentDidMount() {
this.refs.btn.addEventListener('click', this.handleClick);
this.setState({
clicked: this.state.clicked = true,
});
}
componentWillUnmount() {
this.refs.btn.removeEventListener('click', this.handleClick);
this.setState({
clicked: this.state.clicked = false,
});
}
}
This problem is that ESLint keeps telling me "this.refs" is depreciated.
What do I do instead? How can I fix it so it's not using depreciated code?
The Lint rule you are referring to is called no-string-refs and warns you with:
"Using string literals in ref attributes is deprecated (react/no-string-refs)"
You are getting this warning because have implemented the deprecated way of using refs (by using strings). Depending on your React version, you can do:
React 16.3 and later
constructor() {
super();
this.btnRef= React.createRef();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.addVote}><span ref={this.btnRef} className="glyphicon"> </span></div>
</div>
);
}
React 16.2 and older
constructor() {
super();
this.btnRef; //not necessary to declare the variable here, but I like to make it more visible.
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<div>
<div onClick={this.addVote}><span ref={(el) => this.btnRef = el} className="glyphicon"> </span></div>
</div>
);
}
For even better readability, you could also do:
render() {
let myRef = (el) => this.btnRef = el;
return (
<div>
<div onClick={this.addVote}><span ref={myRef} className="glyphicon"> </span></div>
</div>
);
}
Have a look at what the official documentation says on Refs and the DOM, and this section in particular:
Legacy API: String Refs
If you worked with React before, you might be
familiar with an older API where the ref attribute is a string, like
"textInput", and the DOM node is accessed as this.refs.textInput. We
advise against it because string refs have some issues, are considered
legacy, and are likely to be removed in one of the future releases. If
you're currently using this.refs.textInput to access refs, we
recommend the callback pattern instead.
The reason this ESLint rule exists is that string Refs are on their way out. However, for the code above I would recommend to not use a Ref in the first place.
Don't Overuse Refs
React's advantage is that it is declarative. Meaning, we have state and an expression (returned JSX) of how the UI (more precisely the DOM) should look given a certain state.
Whatever can be done using just state and UI expression, should be done this way. The problem with the use of a Ref in the code above is that it makes the code imperative. We can't understand how the DOM will look just from the JSX. Here is how you could achieve the same result in a declarative way:
export class myComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
};
}
handleClick = () => { // with arrow function there is no need for binding.
this.setState(
prevState => {
return {
active: !prevState.active
}
}
)
}
render() {
return (
<div>
<span
onClick={this.handleClick}
className={`glyphicon ${this.state.active && "active"}`}
>
Hello World
</span>
</div>
);
}
}
Refs should be used when state and UI expression aren't enough, and you need access to the actual DOM. For example, focusing on an input field, scrolling to an element, or getting the exact width and height of an element.
If you do use Refs, avoid string refs
String refs harm performance, aren't composable, and are on there way out.
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. [Official React documentation]
[resource1][1], [resource2][1]
Option #1: Use React.createRef
class MyComponent extends Component {
constructor(props) {
super(props)
this.myRef = React.createRef() // create a ref object
}
render() {
return <div ref={this.myRef}></div> // Attach the ref property to a dom element
}
}
Option #2: Use a ref callback
class MyComponent extends Component {
constructor(props){ // Optional, declare a class field
super(props)
this.myRef=null
}
render() {
return <div ref={ (ref) => this.myRef=ref }></div>
} // Attach the dom element to a class field
}
you can try a more declarative way. I changed your code to reflect this. You just need to remind that a component will refresh and call render in every state/props change. So, we can create the class of your element inside render method.
import React from 'react'
export default class myComponent extends React.Component {
constructor() {
super();
this.state = { clicked: false };
this.handleClick = this.handleClick.bind(this);
}
render() {
let btnClass = 'glyphicon'
if(this.state.clicked){
btnClass+=' active'
}
return (
<div>
<div onClick={this.handleClick}><span ref="btn" className={btnClass}> </span></div>
</div>
);
}
handleClick() {
this.setState({
clicked: !this.state.clicked
})
}
}
I am a complete newbie in react native, react.js, and javascript. I am Android developer so would like to give RN a try.
Basically, the difference is in onPress;
This code shows 'undefined' when toggle() runs:
class LoaderBtn extends Component {
constructor(props) {
super(props);
this.state = { loading: false };
}
toggle() {
console.log(this.state);
// let state = this.state.loading;
console.log("Clicked!")
// this.setState({ loading: !state })
}
render() {
return (
<Button style={{ backgroundColor: '#468938' }} onPress={this.toggle}>
<Text>{this.props.text}</Text>
</Button>
);
}
}
but this code works:
class LoaderBtn extends Component {
constructor(props) {
super(props);
this.state = { loading: false };
}
toggle() {
console.log(this.state);
// let state = this.state.loading;
console.log("Clicked!")
// this.setState({ loading: !state })
}
render() {
return (
<Button style={{ backgroundColor: '#468938' }} onPress={() => {this.toggle()}}>
<Text>{this.props.text}</Text>
</Button>
);
}
}
Can you explain me the difference, please?
In Java / Kotlin we have method references, basically it passes the function if signatures are the same, like onPress = () => {} and toggle = () => {}
But in JS it doesn't work :(
The issue is that in the first example toggle() is not bound to the correct this.
You can either bind it in the constructor:
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
...
Or use an instance function (OK under some circumstances):
toggle = () => {
...
}
This approach requires build changes via stage-2 or transform-class-properties.
The caveat with instance property functions is that there's a function created per-component. This is okay if there aren't many of them on the page, but it's something to keep in mind. Some mocking libraries also don't deal with arrow functions particularly well (i.e., arrow functions aren't on the prototype, but on the instance).
This is basic JS; this article regarding React Binding Patterns may help.
I think what is happening is a matter of scope. When you use onPress={this.toggle} this is not what you are expecting in your toggle function. However, arrow functions exhibit different behavior and automatically bind to this. You can also use onPress={this.toggle.bind(this)}.
Further reading -
ES6 Arrow Functions
.bind()
What is happening in this first example is that you have lost scope of "this". Generally what I do is to define all my functions in the constructor like so:
constructor(props) {
super(props);
this.state = { loading: false };
this.toggle = this.toggle.bind(this);
}
In the second example, you are using ES6 syntax which will automatically bind this (which is why this works).
Then inside of you onPress function, you need to call the function that you built. So it would look something like this,
onPress={this.toggle}
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>
);
}
};