React: why do you have to bind this method? - javascript

I'm reading this article on "Lifting State Up" in React. It defines the Calculator component as follows:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
In the line this.handleChange = this.handleChange.bind(this);, I'm wondering why we have to bind this.handleChange to this. It's used in the line onChange={this.handleChange}. Wouldn't this work the same even if we hadn't done that binding?

The this inside handleChange would refer to the method and not to the component instance (Calculator). Since handleChange does not have a setState method (the component does) we have to bind the correct this in the method. If you had another method that was not doing anything with this, then yes, you could skip the bind.
From the official docs:
If you need to have access to the parent component in the handler, you also need to bind the function to the component instance.
A way to circumvent this is to either use the fat arrow syntax (as in Dimitar's answer) or use the React Hook API
In other words (see comments):
constructor(props) {
super(props);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
// ^ this = handleChange. You cannot call setState on handleChange.
}
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
// ^ this = Calculator component. All React (class) Components have setState
}

This has to do with scopes, and is something that ES6 solves by implementing fat arrow functions.
Basically when you create a method inside of a class in JavaScript, that method does not instantly inherit this, so any reference to this will result in an error. To solve this, you need to bind the function to this which basically virtually passes down an instance of the class to the function as a parameter (if you look in the background).
If you want to avoid this binding, you can just use a fat arrow function like so:
handleChange = e => this.setState({[e.target.name]: e.target.value})
In that basic example, I referenced this without having bound the method to this and didnt get an error because fat arrow functions are automatically bound to this

Related

React Higher Order Components Function argument

I don't understand the HOC example in the react docs when it comes to passing in the second argument, selectData. How is that function being passed and utilized,like just general flow of what is happening with selectData?
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
// This function takes a component...
function withSubscription(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
I finally understood. DataSource is a global.
Calling SelectData will trigger the callback provided by the arrow function ie DataSource.getComments().

SetState won't update with callback?

I'm having hard time understanding why the text won't change in browser or why it won't even console.log the new state. I'm just trying to change the text by clicking on it.
class Komponentti extends React.Component{
constructor(props){
super(props)
this.state = {teksti: "Hello"}
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState = ({teksti: "Mello"}), function(){
console.log(this.state.teksti);
}
}
render(){
return(
<h1 onClick={this.handleClick}>{this.state.teksti}</h1>
)
}
}
You're calling it wrong. Should be:
handleClick() {
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
May be you have been confused with es6 fat arrow functions.
In ES6 we can declare the functions using fat arrow notation to pass the lexical this to the function you declare.
Eg:
const example = () => {
// Something interesting
}
But we call that function as example().
But the setState() is an asynchronous function already declared in the React.
We can use it to update the state in the following manner.
handleClick(){
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
Below are the way to set state :
this.setState({valuename:"value"});
this.state.varluename = "value";
this.forceUpdate();

How to avoid using inline functions in React (ES6) for functions that take arguments

I'm trying to follow the suggestion in this react-eslint doc to avoid using inline functions.
I have a div with an onClick funciton like so:
onClick={ () => this.props.handleClick(some_constant) }
This works perfectly fine, however I don't want to have an inline function. When I try to abstract it by following the pattern in the provided link above, I get a setState error that runs infinitely.
class Foo extends React.Component {
constructor() {
super();
this._handleClickWrapper = this.handleClickWrapper.bind(this);
}
_handleClickWrapper() {
// handleClick is a callback passed down from the parent component
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this._handleClickWrapper}>
Hello!
</div>
);
}
}
What needs to be done so that I can avoid using inline functions?
Edit:
I made a serious typo, but in my code, I have what is currently reflected and it is still causing the error.
You bound the wrong function to this. It should be:
this._handleClickWrapper = this._handleClickWrapper.bind(this);
This way _handleClickWrapper will always be bound to the context of the component.
If you really really really want to follow the jsx-no-bind rule, you can create a new component and pass someConstant in as a prop. Then the component can call your callback with the value of someConstant:
class FooDiv extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
render() {
return <div onClick={this.handleClick}>Hello!</div>
}
handleClick() {
this.props.onClick(this.props.someConstant);
}
}
Then your Foo component can just do this:
class Foo extends React.Component {
render() {
const someConstant = ...;
return (
<FooDiv
onClick={this.props.handleClick}
someConstant={someConstant}
/>
);
}
}
Having said that, I would recommend not following jsx-no-bind and just use bind or arrow functions in render. If you're worried about performance due to re-renderings caused by using inline functions, check out the reflective-bind library.
There is a typo
this._handleClickWrapper = this.handleClickWrapper.bind(this);
should be
this._handleClickWrapper = this._handleClickWrapper.bind(this);
in your constructor you forgot to pass props to super()
constructor(props) {
super(props);
this._handleClickWrapper = this._handleClickWrapper.bind(this);
}
Tipp: You can avoid binding (and even the constructor) by using arrow functions declaration inside the class (babel-preset-es2016).
class Foo extends React.Component {
state = {} // if you need it..
onClick = () => {
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this.onClick}>
Hello!
</div>
);
}
}
This way you components gets smaller, and easier to read.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding

ReactJs print value of input field

I'm trying to print instantly my input value in the render function.
In the documentation of the concept of state and lifecycle in a React component, I see the use of a constructor with a super(props) as well as this.state.
I get the error below when trying same;
Uncaught TypeError: Cannot read property 'state' of undefined
Below is my code;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: ''
};
};
handleChange(event) {
this.setState({
text: event.target.value
});
};
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.text}.</h2>
<input type="text" onKeyUp={this.handleChange} />
</div>
);
}
}
How can I fix it?
when you call a function like that, it is called by the window, not by your react object.
To make the function be bound to your react object (and have the ability to use the setState method, you need to use this:
onKeyUp={this.handleChange.bind(this)}
this will bind it to your react object :)
You have to bind this to your event handler like this:
<input type="text" onKeyUp={this.handleChange.bind(this)} />
Working Example: https://codepen.io/shanedaugherty/pen/ALwAzL
this.handleChange = this.handleChange.bind(this);
You can bind it like this in constructor and it will work, as you have to bind this to your react function.
You use value as an attribute.
value={this.state.text}
OR

React component initialize state from props

In React, are there any real differences between these two implementations?
Some friends tell me that the FirstComponent is the pattern, but I don't see why. The SecondComponent seems simpler because the render is called only once.
First:
import React, { PropTypes } from 'react'
class FirstComponent extends React.Component {
state = {
description: ''
}
componentDidMount() {
const { description} = this.props;
this.setState({ description });
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default FirstComponent;
Second:
import React, { PropTypes } from 'react'
class SecondComponent extends React.Component {
state = {
description: ''
}
constructor (props) => {
const { description } = props;
this.state = {description};
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default SecondComponent;
Update:
I changed setState() to this.state = {} (thanks joews), However, I still don't see the difference. Is one better than other?
It should be noted that it is an anti-pattern to copy properties that never change to the state (just access .props directly in that case). If you have a state variable that will change eventually but starts with a value from .props, you don't even need a constructor call - these local variables are initialized after a call to the parent's constructor:
class FirstComponent extends React.Component {
state = {
x: this.props.initialX,
// You can even call functions and class methods:
y: this.someMethod(this.props.initialY),
};
}
This is a shorthand equivalent to the answer from #joews below. It seems to only work on more recent versions of es6 transpilers, I have had issues with it on some webpack setups. If this doesn't work for you, you can try adding the babel plugin babel-plugin-transform-class-properties, or you can use the non-shorthand version by #joews below.
You don't need to call setState in a Component's constructor - it's idiomatic to set this.state directly:
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
// ...
}
See React docs - Adding Local State to a Class.
There is no advantage to the first method you describe. It will result in a second update immediately before mounting the component for the first time.
Update for React 16.3 alpha introduced static getDerivedStateFromProps(nextProps, prevState) (docs) as a replacement for componentWillReceiveProps.
getDerivedStateFromProps is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
It is static, therefore it does not have direct access to this (however it does have access to prevState, which could store things normally attached to this e.g. refs)
edited to reflect #nerfologist's correction in comments
You could use the short form like below if you want to add all props to state and retain the same names.
constructor(props) {
super(props);
this.state = {
...props
}
//...
}
YOU HAVE TO BE CAREFUL when you initialize state from props in constructor. Even if props changed to new one, the state wouldn't be changed because mount never happen again.
So getDerivedStateFromProps exists for that.
class FirstComponent extends React.Component {
state = {
description: ""
};
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.description !== nextProps.description) {
return { description: nextProps.description };
}
return null;
}
render() {
const {state: {description}} = this;
return (
<input type="text" value={description} />
);
}
}
Or use key props as a trigger to initialize:
class SecondComponent extends React.Component {
state = {
// initialize using props
};
}
<SecondComponent key={something} ... />
In the code above, if something changed, then SecondComponent will re-mount as a new instance and state will be initialized by props.
set the state data inside constructor like this
constructor(props) {
super(props);
this.state = {
productdatail: this.props.productdetailProps
};
}
it will not going to work if u set in side componentDidMount() method through props.
If you directly init state from props, it will shows warning in React 16.5 (5th September 2018)
you could use key value to reset state when need, pass props to state it's not a good practice , because you have uncontrolled and controlled component in one place. Data should be in one place handled
read this
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
You can use componentWillReceiveProps.
constructor(props) {
super(props);
this.state = {
productdatail: ''
};
}
componentWillReceiveProps(nextProps){
this.setState({ productdatail: nextProps.productdetailProps })
}

Categories