How can I bind function with hooks in React? - javascript

Basically we bind event handler functions in constructor or make them as arrow functions in React class components like below
class Test extends Component{
constructor(props){
super(props);
this.state = { count:0 };
this.setCount = this.setCount.bind(this);
}
setCount() {
this.setState({count: this.state.count + 1});
}
render() {
return <button onClick={this.setCount}>Increase</button>
}
}
But after hooks are introduced in React v16.7.0 the class components became functional components with state.
So how can I bind the function with hooks in functional component?

There's no need to bind functions/callbacks in functional components since there's no this in functions. In classes, it was important to bind this because we want to ensure that the this in the callbacks referred to the component's instance itself. However, doing .bind in the constructor has another useful property of creating the functions once during the entire lifecycle of the component and a new callback wasn't created in every call of render(). To do only initialize the callback once using React hooks, you would use useCallback.
Classes
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={this.handleClick}>Click Me</Button>;
}
}
Hooks
function Foo() {
const memoizedHandleClick = useCallback(
() => {
console.log('Click happened');
},
[], // Tells React to memoize regardless of arguments.
);
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}

People come to SO and copy-paste code. Leaving this answer here, so the React community doesn't go around incorrectly memoizing everything and potentially doing more work than necessary.
Function components
function Foo() {
const handleClick = function(){
// use function statements to avoid creating new instances on every render
// when you use `bind` or arrow functions
console.log('memoizing can lead to more work!')
};
return <Button onClick={handleClick}>Click Me</Button>;
}
Tip: look at what code is transpiled when using useCallback and see if it's necessary, before dropping it in. if you're not sure you need it, you probably don't. and to be sure it's doing you good, profile it.

You might as well write the component Foo above like this and save yourself some typing. Note the syntax around handleClick ... it defines the closure handleClick as a field on Foo, rather than as a method. This removes the need for you to use bind to overwrite the OBject's 'handleClick' reference in the constructor. (Also, you don't need to define a constructor if you're just calling 'super'!)
class Foo extends Component {
handleClick = () => {
console.log('Click happened');
}
render() {
return <Button onClick={this.handleClick}>Click Me</Button>;
}
}
Similarly, for your original example, just declare state and setCount directly and to simplify your code:
class Test extends Component{
state = {count: 0}
setCount = () => {
this.setState({count: this.state.count + 1});
}
render() {
return <button onClick={this.setCount}>Increase</button>
}
}

Related

React onClick, Cannot read property 'bind' of undefined

I'm trying to run a onclick function in react
My button:
<button onClick={this.logOut.bind(this)}>LogOut</button>
My function
function logOut(){
Auth.signOut()
.then(data => console.log(logout)
.catch(err => console.log(err));
}
but the an error comes back Cannot read property 'bind' of undefined
Firstly, never bind in your render method. bind returns a new method each time you call it, which means that react cant optimise your render method. React looks at the props of components to see if anything has changed, and with functions, it compares equality by object reference. Since your function will be a new function each time, react will think things are changing.
Secondly, you can use an arrow function in your component to avoid the need for bind all together.
Here are two ways you can write this differently, first with bind:
class MyComponent extends Component {
constructor() {
this.logOut = this.logOut.bind(this);
}
function logOut() {
//...
}
function render() {
return <button onClick={this.logOut}>LogOut</button>
}
}
Secondly with an arrow function:
class MyComponent extends Component {
logOut = () => {
//...
}
function render() {
return <button onClick={this.logOut}>LogOut</button>
}
}
Would be helpful if you can please give more details .
Just tried an example in codepen and I see it working fine.
https://codepen.io/anon/pen/qKBdaB?editors=0011
class Toggle extends React.Component {
handleClick() {
console.log('Hello')
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>
SayHello
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

React - Setting component state using a function outside of state, is it wrong?

Is it wrong to use setState in a function outside of the React component?
Example:
// myFunction.js
function myFunction() {
...
this.setState({ ... })
}
// App.js
import myFunction from './myFunction
class App extends Component {
constructor() {
super()
this.myFunction = myFunction.bind(this)
}
...
}
I'm not sure the way you're binding will actually work. You could do something like:
export const getName = (klass) => {
klass.setState({ name: 'Colin'})
}
then
class App extends Component {
state = {
name: 'React'
};
handleClick = () => {
getName(this);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.handleClick}>change name</button>
</div>
);
}
}
Working example here.
So the only reasons to do this is if you are reducing repeated code, e.g. two components use the same logic before calling this.setState, or if you want to make testing easier by having a separate pure function to test. For this reason I recommend not calling this.setState in your outside function, but rather returning the object you need so it can you can call this.setState on it.
function calculateSomeState(data) {
// ...
return { updated: data };
}
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = calculateSomeState(props.data);
}
handleChange = (e) => {
const value = e.target.value;
this.setState(calculateSomeState({ ...props.data, value }));
}
}
It looks like a bug waiting to happen... If you want to use an external function to set state, you can use the alternative syntax provided by React:
this.setState((prevState, props) => {
return updatedState; //can be a partial state, like in the regular setState
});
That callback can easily be extracted to an external function and it's guaranteed to work
It is not wrong, the function is never called outside the component. This is a mix-in technique. bind isn't needed, as long as the function isn't used as a callback. In this case myFunction is same among all instances, a more efficient way would be:
class App extends Component {}
App.prototype.myFunction = myFunction;

how come class methods don't behave normally with react components?

Given a component like this :
class App extends Component {
state = {}
sayHello() {
// 'this' is undefined here... It should point to the component instance
}
render() {
return (
<div onClick={this.sayHello}>
clickMe
</div>
);
}
}
how come sayHello can't access this ? That is not the expected behavior of an ES6 class.
What am I missing out on ?
In javascript this depends on how you call the method. This can be changed by binding this inside a method. To illustrate what is happening in your example:
class App {
sayHello() {
console.log(this)
}
}
const a = new App()
a.sayHello() // A {}
const callback = a.sayHello // what you are passing as a callback to onClick
callback() // undefined
To fix this problem you need to bind this in sayHello function. You can use class properties to achieve this. This is still an experimental feature of javascript but seems to be a community accepted way of binding this for React components.
class App {
sayHello = () => {
console.log(this)
}
}
callback() // A {}
div it self is another Class so when calls your sayHello inside himself it will change this.
All you have to do is bind this to sayHello or call it via ES6 arrow function.
<div onClick={ this.sayHello.bind(this) }>
or
<div onClick={ (event) => this.sayHello(event) }>

Calling a function in React

I'm a beginner in React, and I'm a little confused about calling a function in React.
I saw the following ways and I don't know when to use each and which one.
handleAddTodo ={this.handleAddTodo}
handleAddTodo ={this.handleAddTodo()}
handleAddTodo ={handleAddTodo}
handleAddTodo ={this.handleAddTodo}
handleAddTodo ={handleAddTodo()}
Are these interchangeable? Could I do that to handle an event, the same way to call a function?
Are these interchangeable?
Short answer: No.
Let's take a look at the different snippets you've posted:
someFunction() vs someFunction
With the former syntax, you are actually invoking that function. The latter is just a reference to that function. So when do we use which?
You would use someFunction() when you want that function invoked and its result returned immediately. In React, this is typically seen when you split parts of your JSX code to a separate function; either for reasons of readability or reusability. For example:
render() {
myFunction() {
return <p>Foo Bar</p>;
}
return (
<div>
{myFunction()}
</div>
);
}
You would use someFunction when you want only to pass the reference to that function to something else. In React, this is usually an event handler that is passed down to another child-component via props so that that component can call the event handler when it needs to. For example:
class myApp extends React.Component {
doSomething() {
console.log("button clicked!");
}
render() {
return (
<div>
<Button someFunction={this.doSomething} />
</div>
);
}
}
class Button extends React.Component {
render() {
return (
<button onClick={this.props.someFunction}>Click me</button>
);
}
}
someFunction() vs this.someFunction()
This has to do with the context of the function. Basically, "where is this function?". Is part of the current Component, then use this.someFunction(), is it part of the parent Component passed in as props, then use this.props.someFunction(). Is it a function inside the current method, then just use someFunction().
Obviously, there's a lot more to it than that, but it's the best basic summary I can give.
For a better understanding, have a read here. It is a great guide to how the this keyword works in Javascript and in React in particular.
If you want to call a function options 2 and with some assumptions 5 should work.
If you want to actually pass a function as a property to some child component so that it could call it later (say to notify your root element on some event) then option 1 (with prebind) and 3 (with defining a variable const {handleAddTodo} = this and prebind :) ) should work
// this works if handleAddTodo was prebinded or doesn't use this
handleAddTodo ={this.handleAddTodo}
// this probably wont work unless handleAddTodo is higher order function that returns another function
handleAddTodo ={this.handleAddTodo()}
// This wont work unless you have a var/let/const that is referencing a function
handleAddTodo ={handleAddTodo}
// Same as 1
handleAddTodo ={this.handleAddTodo}
// 3 and 2 combined
handleAddTodo ={handleAddTodo()}
To call the function you have to add ()
{this.handleAddTodo()}
About handling events - Handling#Events
Arrow Functions - Functions#ArrowFunctions
In ES6 you can use normal function or Arrow Function:
Function1 (Normal Function)
functionA(){
//Something here
}
Then should call this.functionA()
Function2 (ArrowFunction)
functionA = () => {
//SomeThing Here
}
Then should call this.functionA
*Function3 (Eg: in a const of React) *
const A = (functionTest) =>{
return (
<div>{functionTest}</div>
);
}
functionTest is mapStateToProps in React :)
I hope it is helpful for you :)
this is correct -> handleAddTodo ={this.handleAddTodo}
When function passing to child component you have to bind your function like this handleAddTodo ={this.handleAddTodo.bind(this)}. below code help out your doubt.
Simple Example
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: 'Initial data...'
}
this.updateState = this.updateState.bind(this);
};
updateState() {
this.setState({data: 'Data updated...'})
}
render() {
return (
<div>
<button onClick = {this.updateState}>CLICK</button>
<h4>{this.state.data}</h4>
</div>
);
}
}
export default App;
Child Events
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: 'Initial data...'
}
this.updateState = this.updateState.bind(this);
};
updateState() {
this.setState({data: 'Data updated from the child component...'})
}
render() {
return (
<div>
<Content myDataProp = {this.state.data}
updateStateProp = {this.updateState}></Content>
</div>
);
}
}
class Content extends React.Component {
render() {
return (
<div>
<button onClick = {this.props.updateStateProp.bind(this)}>CLICK</button>
<h3>{this.props.myDataProp}</h3>
</div>
);
}
}
export default App;
Refer here
You can trigger events with this.props.someProps(). Check the following sample.
import React, { Component } from 'react';
class AddToDo extends Component {
render() {
return (
<button onClick={ev => this.props.handleAddToDo(ev, 'hello')}>
{this.props.title}
</button>
)
}
}
class Todos extends Component {
handleAddToDo(ev, someVal) {
// do something
}
render() {
return (
<AddToDo title="Add" handleAddToDo={(ev, someVal) => this.handleAddToDo(ev, someVal)} />
)
}
}
export default Todos;

Component not mounted when setState called in arrow function in React with ES2016

Note: I have restructured my question to make it more understandable. However, I have kept the older version for historical purposes.
I have a React application and when I call this.setState() from an arrow function, I get the "component not mounted message". Here is my component:
import React, { Component } from 'react'
class Test extends Component {
state = {
value: ''
}
onChange = (e) => {
e.preventDefault()
this.setState({
value: e.target.value
})
}
render() {
return <input value={ this.state.value } onChange={ this.onChange } />
}
}
When I type into the input, I get the following error message:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
I am not sure why this is, because the arrow function should not have to be binned, and since this is triggered by typing, the component is obviously mounted.
Thanks for any help!
Here is the previous version of this question:
I have a pretty standard React application and when I call this.setState() from an
arrow function, I get the "component not mounted message".
The code looks something like this:
onClick = () => {
this.setState({clicked: true})
}
...
render() {
return <AnotherComponent onClick={ this.onClick } />
}
When onClick is called, I get this message:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
I'm not sure what is causing this, because I am using an arrow
function but it is still not working.
EDIT: As asked, here is AnotherComponent:
const AnotherComponent = (props) => {
return (
<div>
<Button primary>Reject</Button>
<Button primary onClick={ props.onClick }>Edit Post</Button>
<Button primary>Approve</Button>
</div>
)
}
Thanks
When you say onClick={ this.onClick } you are saying "here is a function to use". However, it loses its context.
You need to bind() the context of onClick() before you pass it into your attribute. The best place to do this is in the constructor of your class.
class MyClass extends React.Component {
constructor(props) {
super(props); // have to do this part in any Component constructor
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState({ clicked: true });
}
render() {
return <AnotherComponent onClick={ onClick } />;
}
}
Alternatively, if your function is short, you can just create it in the constructor:
constructor(props) {
super(props);
this.onClick = () => { this.setState({ clicked: true }); };
}
By being creating in the context of the constructor, it'll implicitly be bound.
Your component is perfectly correct, now we only have to know where are you calling it from.
This issue usually comes from the parent, just check if the parent is fully mounted.
Matter of fact:
onChange doesn't need the e.preventDefault(),
as each time you type on the input, it invokes setting the state,
every time thestate gets set,
It triggers the lifecycle method render() again ( it reloads the node), so in this case.
when you preventDefault(), you're breaking the regular flow of lifecyle components :)
remove it and the warning will go away.
====================PART 2 ===========================
Oh I see now, everything is clear :D
Whenever you call the onClick = () => {
this.setState({clicked: true})
}
...
render() {
return <AnotherComponent onClick={ this.onClick } />
}
You're wanting to pass down the function through props
but you're setting it to the onClick event, and it's a reserved word
You should pass it through a reserved prop like:
return <AnotherComponent handleClick={ this.onClick } />
and then on the child:
<Button primary onClick={ props.handleClick }>Edit Post</Button>
now you created a an actual property
Hope it helped you :)

Categories

Resources