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

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) }>

Related

How can I bind function with hooks in React?

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>
}
}

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;

React - How do you call a function from inside another function

Suppose I had this layout below:
class Navigation extends React.Component {
primaryFun() { console.log('funn') }
secondaryFun() {
this.primaryFun();
}
}
I'd of expected this to then call primary but instead I get an undefined, Ok.
So I thought I'd add a constructor to bind the function to this:
constructor(props) {
super(props)
this.primaryFun = this.primaryFun.bind(this);
}
but primary fun is still undefined.
In my real project I'm calling these on a mouseOut event.
Feels like the above should work and tbh the documentation for React is all over the shot so couldn't find much here.
Are you looking for something like this calling one function inside the other
import React, { Component } from 'react';
import './App.css'
class App extends Component {
constructor(){
super()
this.mouseClick = this.mouseClick.bind(this);
this.primaryFun = this.primaryFun.bind(this);
this.secondaryFun = this.secondaryFun.bind(this);
}
primaryFun(){
console.log('primaryFun funn')
}
secondaryFun(){
console.log('secondaryFun funn')
this.primaryFun()
}
mouseClick(){
this.secondaryFun()
}
render() {
return (
<div onClick={this.mouseClick}>
Hello World!
</div>
);
}
}
export default App;
Here when you click on "Hello world" secondaryFun is called and inside secondaryFun , primaryFun is been triggered
You also need to bind the secondaryFun function to use this inside that. Without that, the this inside the function secondaryFun will refers to the function scope which is secondaryFun
You need to bind this in your mouseOut
onMouseOut={this.secondaryFun.bind(this)}
Or a as best practice use the Lambda syntax. It'll bind this for you
onMouseOut={()=>this.secondaryFun()}
Make sure both functions have the correct this scope. If you are using class properties, see https://babeljs.io/docs/plugins/transform-class-properties/. Which already present on the babel-preset-react-app used by create-react-app, you can use that and write those as arrow functions, as seen on the babel link. And avoid having to use .bind on the constructor.
You must bind() both two functions.
You should that:
class Navigation extends React.Component {
constructor(props) {
super(props)
this.primaryFun = this.primaryFun.bind(this);
this.secondaryFun = this.secondaryFun.bind(this);
}
primaryFun() {
console.log('funn')
}
secondaryFun() {
this.primaryFun();
}
}

Why are there different syntax of methods inside extending React.Component?

I notice that inside class ExampleComponent extends React.Component {...} there are different ways of defining methods, with the former being a declaration for methods that is part of React and the latter being expressions for your own methods. Why is this? Why aren't they both in the same format?
componentDidMount() {
...
}
vs.
myMethod = () => {
...
}
This one goes to prototype
fnProto() {
}
This one is experimental and goes directly to instance having this always refering to the instance.
fnInstance = () => {}
Translating to ES5
class Cmp {
fnProto() {
console.log('goes to proto')
}
fnInstance = () => {
console.log('goes to instance')
}
}
Will be roughly equivalent to
function Cmp() {
this.fnInstance = (function() {
console.log('goes to instance')
}).bind(this)
}
Cmp.prototype.fnProto = function() {
console.log('goes to proto')
}
When you have
componentDidMount() {
...
}
it is a lifecycle function and this inside it is automatically bound by default to the React Component context.
However when you define your own function, this inside it will refer to the content of the function itself. However if you define it using an arrow function like
myMethod = () => {
...
}
this keyword inside it will refer to the parent context which in this case is the React Component context.
Check this article on Arrow function

Categories

Resources