React this.setState in regular method notation - reactjs

I am a React newbie, and am working on examples from Adam Freeman's book.
I am starting with simple event handling and am unable to figure out why the regular-looking version of handleClick() below does not work. The method using the arrow notation (from the book) works as expected, but I am trying to translate it to the standard method notation, and am unable to figure out what is missing.
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 4
}
}
isEven(val) { return val % 2 === 0 ? "Even" : "Odd"; }
// the following works
//handleClick = () => this.setState({ count: this.state.count + 1 });
// this gives an error: TypeError: Cannot read property 'setState' of undefined
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render = () =>
<h4>
<button onClick={this.handleClick}>Click me!</button>
Number of items: {this.isEven(this.state.count)}
</h4>
}
What changes are needed for handleclick() to work?

You can bind this using one of the below,
In Constructor,
constructor(props) {
super(props);
this.state = {
count: 4
}
this.handleClick = this.handleClick.bind(this);
}
Or you can directly bind this as,
<button onClick={this.handleClick.bind(this)}>Click me!</button>
Or simply using fat arrow syntax,
<button onClick={() => this.handleClick()}>Click me!</button>
Ref

Related

Why this event handling function is being called twice and updates the state twice in react?

import React, { Component } from 'react'
class TestState extends Component{
constructor(props){
super(props)
this.state = {
count:1
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
console.log(this.state)
this.setState (state=>{
return state.count++
})
}
render(){
return <div>
<button onClick={this.handleClick} >Click</button>
{this.state.count}
</div>
}
}
export default TestState
In the above code, when I click the button the counter increases by double value every time I click it .. e.g. On clicking the button the count will increase by 1, 3, 5, 7?
But state.count should only increase once because of the ++ operator.
you should update the state like this
handleClick(){
console.log(this.state)
this.setState (state=>{
return { count: state.count + 1}
})
}
because you return a object that you wish update not a integer
The first answer is a solution, but the lifecycle method can be executed another way:
In my experience, ES6 fashion lends a DRY approach to updating state. Use of anonymous functions and implicit returns allow omission of the return keyword even though they aren't required if you don't rely on the value for another calculation ( Not recommended due to #setState's async nature ).
handleClick() {
console.log( this.state )
this.setState( state => ({
counter: state.count + 1
}))
}
For more on the matter see: https://reactjs.org/docs/state-and-lifecycle.html
Mutating state directly, as you do in your example, can lead to odd behavior and is never advisable. Here is a quote from the React docs:
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
By doing this state.count++ you throw React for a loop and as such see unexpected results.
I would rewrite your code like this: (https://codesandbox.io/s/red-lake-yk9gy?file=/src/App.js):
import React, { Component } from "react";
class TestState extends Component {
constructor(props) {
super(props);
this.state = { count: 1 };
}
handleClick = count => {
count++;
this.setState({count});
};
render() {
const { count } = this.state;
return (
<div>
<button onClick={() => this.handleClick(count)}>Click</button>
{count}
</div>
);
}
}
export default TestState;

How to dynamically update child component properties

I'm trying to use a video player (that could show any number of different videos) in this lightbox that accepts components as well as images:
lightbox-react
Their docs give a simple example (pasted in below) that overlooks an important detail: a component in the lightbox may need to have parts of its content changed when displayed. So I need understand how to pass properties to the component that's being used for the mainSrc property of the lightbox.
I'm new to React, so I want to learn the right way to manage this. The only syntax I can find that will work for me is, in the parent:
constructor(props) {
super(props);
this.state = {
myVidPlayer: '',
}
}
...
handleLightboxOpen() {
this.setState({
// re-create new video player every time:
myVidPlayer: React.createElement(VidPlayer, { vidSrc: **real video source here** }),
});
}
And in the render of the lightbox for the parent, I simply have <Lightbox mainSrc={this.state.myVidPlayer}...> which does work as needed, but I'm recreating the child component every time the lightbox pops up, which seems wrong.
I tried to do this:
constructor(props) {
super(props);
this.state = {
myVidSrc: '',
}
this.vidPlayer = React.createElement(VidPlayer, { vidSrc: this.state.myVidSrc });
}
...
handleLightboxOpen() {
this.setState({
// does not work, child does not get updated:
myVidSrc: '**real video source here**',
});
}
And in the render of the lightbox for the parent, <Lightbox mainSrc={this.vidPlayer}...> but the child component "vidSrc" prop never gets updated when the parent state variable is changed. I had been under the impression that this should bubble down, but in this case, the prop seems to remain at its initial value.
Their example code is as follows (I'm replacing "VideoIframe" with my own "vidPlayer" per above):
import React, { Component } from 'react';
import Lightbox from 'lightbox-react';
import VideoIframe from 'components/cat-video';
const images = [
VideoIframe,
'//placekitten.com/1500/500',
'//placekitten.com/4000/3000',
'//placekitten.com/800/1200',
'//placekitten.com/1500/1500'
];
export default class LightboxExample extends Component {
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false
};
}
render() {
const {
photoIndex,
isOpen,
} = this.state;
return (
<div>
<button
type="button"
onClick={() => this.setState({ isOpen: true })}
>
Open Lightbox
</button>
{isOpen &&
<Lightbox
mainSrc={images[photoIndex]}
nextSrc={images[(photoIndex + 1) % images.length]}
prevSrc={images[(photoIndex + images.length - 1) % images.length]}
onCloseRequest={() => this.setState({ isOpen: false })}
onMovePrevRequest={() => this.setState({
photoIndex: (photoIndex + images.length - 1) % images.length,
})}
onMoveNextRequest={() => this.setState({
photoIndex: (photoIndex + 1) % images.length,
})}
/>
}
</div>
);
}
}
Thanks for providing that update.
Passing props from a parent to a child component is a fundamental aspect of react and is very important to understand. A simple example of this might look like such;
Child = React.createClass({
render() {
return <div>{this.props.someProp}</div>;
}
});
Parent = React.createClass({
render() {
return <Child someProp={"an example value"}/>;
}
});
Mounting the Parent will now result in <div>an example value</div> being rendered to the display.

How to onclick method set for only one input?

First please click for SS.
Right now I have 2 input which has value credit-card and paypal.
I set an onClick event for CreditCard to provide card informations.It works fine but problem is:
Card details doesn`t disappear when I click paypal input. It works just if I click CreditCart input again. I want to make it disappear even I click paypal input. I mean card details should seem only by clicking Credit Card input.
class CreditCart extends React.Component {
constructor(props) {
super(props);
this.state = {show:false};
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.setState({ show : !this.state.show})
}
render () {
return (
//2 input here credir-cart has onClick
{this.state.show && <CreditCart/>
}
Second component which includes cart information part:
class CreditCart extends React.Component {
constructor(props) {
super(props);
}
render () {
// information part
}
Your handleClick method is wrong. The setState method is asynchronous and it will try to execute it in batches which means that the previous value might not be updated yet.
Change it to
handleClick() {
this.setState(function (prevState, props) {
return {
show: !prevState.show
};
});
}
See State Updates May Be Asynchronous
I think something like the following should work for you...
class SomeComponent extends Component {
constructor() {
this.state = {
toggle: false
};
}
render() {
return (
<div>
{this.state.toggle
? <CreditCardComponent />
: <PaypalComponent />}
<button
onClick={e => this.setState({ toggle: !this.state.toggle })}
>
Toggle
</button>
</div>
);
}
}

React - Why is binding this not required in this example?

Trying to figure out the basics of React.
Looking at the second example on this page: https://facebook.github.io/react/
I see that the tick() function sets the state of the Timer class, incrementing the previous value by one.
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {secondsElapsed: 0};
}
tick() {
this.setState((prevState) => ({
secondsElapsed: prevState.secondsElapsed + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
}
ReactDOM.render(<Timer />, mountNode);
However, when I tried to implement my own simple Counter class, it failed and I got a console error saying Cannot read property setState of undefined.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
increment(prevState) {
this.setState((prevState) => ({
count: prevState.count + 1
}));
}
render() {
return (
<div className="main">
<button onClick={this.increment}>{this.state.count}</button>
</div>
)
}
}
Some Googling reveals that I have to bind this to the increment function. But why was that not required in the first example that I saw? I copied the code to CodePen and it ran fine with React 15.3.1 I cannot find anything resembling binding in that example. Only after I added binding code in the constructor did things start working in my example.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
// THIS ONE LINE FIXED THE ISSUE
this.increment = this.increment.bind(this);
}
increment(prevState) {
this.setState((prevState) => ({
count: prevState.count + 1
}));
}
render() {
return (
<div className="main">
<button onClick={this.increment}>{this.state.count}</button>
</div>
)
}
}
Answering your question: the first example uses arrow functions, that automatically performs context binding. From the docs:
An arrow function does not create its own this context, so this has
its original meaning from the enclosing context.
Indeed there are some ways of binding in React:
1) you can bind all functions in your constructor, like you said:
constructor(props) {
/* ... */
this.increment = this.increment.bind(this);
}
2) invoke your callbacks with arrow functions:
<button onClick={(e) => this.increment(e)}>
3) append .bind at the end of your method reference each time you set it as a callback, like this:
<button onClick={this.increment.bind(this)}>
4) In your class, define the method with arrow functions:
increment = (e) => {
/* your class function defined as ES6 arrow function */
}
/* ... */
<button onClick={this.increment}>
In order to use this syntax with babel, you have to enable this plugin or use stage-2 preset.
If you look closely at the way the tick() function has been called in your fist example, you will understand that binding has been specified to it when it is called using the arrow functions. If you do the same for the increment function it will also work. These are just different ways of binding the functions.
So as asked, its not that in the first example no binding is specified while the second it requires, rather in both your cases you are binding just that the way of binding is different for both the cases.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
}
increment(prevState) {
this.setState((prevState) => ({
count: prevState.count + 1
}));
}
render() {
return (
<div className="main">
<button onClick={() => this.increment()}>{this.state.count}</button>
</div>
)
}
}
ReactDOM.render(<Counter/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Binding event handlers in React Components fires onClick when rendered

I am learning React. In a test app I'm writing, I am rendering some buttons with onClick methods. When they are rendered like this, they work and call the selectMode function as expected when clicked.
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
}
...
selectMode(mode) {
this.setState({ mode });
}
render() {
...
return (<div>
<button onClick={this.selectMode.bind(this, 'commits')}>Show Commits</button><br/>
<button onClick={this.selectMode.bind(this, 'forks')}>Show Forks</button><br/>
<button onClick={this.selectMode.bind(this, 'pulls')}>Show Pulls</button>
</div>
)
}
But when I tried the suggested best practices way shown below, by binding in the constructor, the selectMode function is called three times when the component is rendered. Why are the onClick event handlers being called then? What do I have wrong?
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
this.selectMode = this.selectMode.bind(this)
}
...
selectMode(mode) {
this.setState({ mode });
}
render() {
...
return (<div>
<button onClick={this.selectMode('commits')}>Show Commits</button><br/>
<button onClick={this.selectMode('forks')}>Show Forks</button><br/>
<button onClick={this.selectMode('pulls')}>Show Pulls</button>
</div>
)
}
your this.selectMode(...) is executed IMMEDIATELY whenever your component is rendered.
<.. onClick={this.selectMode('commits')}..../> <-- function is called immediately
You can use arrow function to create an anonymous function in which you can call your method. In this way, you this.selectMode method only get called when the click event occurs :
<.. onClick={() => this.selectMode('commits')}..../>
If you don't want to create anonymous functions everytime you render the component, you can store an value to an attribute of the element. Like this :
constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
this.selectMode = this.selectMode.bind(this)
}
selectMode(event){
this.setState({mode: e.target.name});
}
render(){
....
<.. onClick={this.selectMode} name='commits' ..../>
..}
I'm not sure, but I think it's because you call upon each onClick function by adding the parentheses. If you use ES6 you could try doing this:
onClick = () => { this.selectMode('commits') }
onClick = () => { this.selectMode('forks') }
onClick = () => { this.selectMode('pulls') }

Resources