React - binding `this` to an imported function - javascript

In my React app, I have a handful of functions that I'd like to be able to access across a few similar components... however, I want to bind this to the shared functions so that they can do things like update the component state, etc... however, it seems that importing the functions and then trying to bind this in the 'typical' React manner does not work.
Here's an illustration of what I'd like to accomplish - in this case, clicking the rendered button would call the function from the imported shared function file and update the component state:
//shared_functions.js
const sharedFunctions = {
testFunction = () => {
this.setState({functionWasRun: true})
}
}
//MyComponent.jsx
import React, { Component } from 'react';
import sharedFunctions from '../static/scripts/shared_functions.js';
let { testFunction } = sharedFunctions;
class MyComponent extends Component {
constructor(props){
super(props);
this.testFunction = this.testFunction.bind(this)
this.state = {
functionWasRun: false
}
}
render(){
return(
<div>
<button onClick={this.testFunction}>Click</button>
</div>
)
}
}
Trying to run this code as is will return an error like:
Uncaught (in promise) TypeError: Cannot read property 'bind' of undefined
and I get what that's all about... but what I'd like to know is: is it possible to bind this to an imported function?
I'm starting to get a lot of similar-looking functions popping up throughout my app and I'd love to simplify things by abstracting them into a shared script, but I'm not sure how to achieve the typical this binding that's needed to achieve state-setting.

The following line is not trying to bind the imported testFunction but rather a method testFunction of <MyComponent>
To bind the imported function, refer to it directly, as follows:
this.testFunction = testFunction.bind(this);
// Notice how: ^--- there is no longer a this here
NB: You're example tries to use bind on an arrow function You cannot bind a new context to an arrow function. The this context of an arrow function will always be set to the location
were it is defined. You can get around this by declaring
testFunction using a regular function declaration:
const sharedFunctions = {
function testFunction(){
this.setState({functionWasRun: true})
}
}

I used it in the following way:
In constructor:
this.handleChange = handleChange.bind(this);
In the imported file (be careful, no arrow):
export const handleChange = function handleChange(event)
{
const { name, value } = event.target;
this.setState({
[name]: object
});
};

import ToastTimeout from 'toastTimout'
ToastTimeout.bind(this)('message to popup')
I was able to get the context of this in a simple service with a setTimeout function that changed the variable on this context
sorry for the sudo code

Related

Why do we need to use bind() in ReactJS to access this.props or this.state? [duplicate]

This question already has answers here:
Why and when do we need to bind functions and eventHandlers in React?
(2 answers)
Closed 3 years ago.
look at this code for example
import React, { Component } from ‘react’;
class App extends Component {
constructor(props) {
super(props);
this.clickFunction = this.clickFunction.bind(this);
}
clickFunction() {
console.log(this.props.value);
}
render() {
return(
<div onClick={this.clickFunction}>Click Me!</div>
);
}
}
what's the purpose of bind(this) ? it binds the function clickFunction to the context of the object which clickFunction is already bound to , let me illustrate what i am trying to say with a normal javascript code :
class my_class {
constructor() {
this.run = this.run.bind(this)
}
run() {
console.log(this.data)
}
}
my_class.data = 'this is data'
new my_class().run() //outputs 'undefined'
and if you remove bind(this) it will give you the same results
constructor() {
this.run = this.run
}
results :
new my_class().run() //still outputs 'undefined'
i am sure i am understanding something wrong and this might the worst question on earth however i am new to es6 and i am not used to classes yet so i apologize for that
Blame JavaScript not React. This is done to retain object instance when the function is going to be passed. Certainly, it must be semantically correct for the function to expect such object. Most common case is to bind this when passing object method. The keyword This depends on how the function is called not how/where it is created. The relationship to This should be maintained at invocation.
Consider:
class Welcome extends React.Component {
render() {
return <button onClick={this.sayName}>Say My
Name</button>;
}
sayName() {
alert(this.props.name);
}
}
In React, you invoke like this: . This renders a button. Clicking the button should trigger an alert with "Bob".
Except it doesn't. Because in the above example, this would be undefined in the sayName function.
What's happening inside the render function is that this refers to the current instance of our React component. That component has a sayName function defined, so this.sayName points to our function, just fine and dandy.
But what React is doing behind the scenes is assigning this.sayName to another variable. That is, it's just like this:
let onClick = this.sayName;
onClick(); // Technically a click event is passed
to onClick
// but this doesn't matter for our purposes
We get an error. Because this is undefined. This is
extra confusing because in previous versions of React, React would "autobind" the event handler for you, so it would work. But at some point, Facebook decided to stop doing that, so ... here we are.
So how can we fix our component? We just do binding ourselves, like this:
<button onClick={this.sayName.bind(this)}>Say My
Name</button>;
Or with ES6 syntax:
<button onClick={() => this.sayName()}>Say My
Name</button>;
And it should work!

JS Class that return a function

So i love this React thingy, and we have this so called 'stateless' functions to help us build a 'dumb' component. Now i want to create a class that produce a stateless function. Why you may ask, well i love the idea of inheritance and wanting to 'extend' my stateless function basic capability, said i want to add a helper function as a statics that binds to the function itself.
I ended up with this code
class Stateless {
constructor() {
return this.render.bind(this)
}
nanny() {
// do something
}
render(props) {
// yeay! a stateless function!
// plus i can access nanny by using this.nanny()
}
}
And when i extend it, i can see that the inheritance is working well.
BUT, if then i initialize the class:
const stateless = new Stateless()
Why can't i access stateless.nanny even tho inside the render function i can see that this.nanny is accessible? Where does the nanny lives? Does it binded to the render function?
EG:
class Stateless {
constructor() {
return this.render.bind(this)
}
nanny() {
console.log('foo')
return true
}
render(props) {
console.log(this.nanny()) // -> returns 'foo'
return 'JSX'
// this should return a JSX
}
}
const stateless = new Stateless() // -> stateless IS a function
stateless()
// 'foo'
// true
// JSX
stateless.nanny
// undefined
While clearly inside render when i called this, there is nanny there. But when i
refer it outside, the nanny is gone. I thought nanny should be a static property of the stateless, right?
If you are returning object from constructor - new will return that object instead of the instance of the class being constructed (more info).
Therefore line
const stateless = new Stateless()
will assign to stateless variable result of this.render.bind(this) - that is reference to method (function) of Stateless class, that is not an instance of Stateless. Therefore stateless.nanny makes no sense - as function render does not have such property defined. On the other hand calling bound render function directly - produce the expected result.
All in all - i strongly do not recommend you to return anything from constructor (unless you are dealing with some really weird requirements like controlling number of instances and such). It makes code hard to understand and maintain.
Your example should work if you remove this.render.bind(this) from your constructor.
It should also work, if you just remove return from the constructor:
constructor() {
this.render.bind(this)
}
However, you might actually be looking to create a higher order component that can enhance the component that it wraps.
Your higher order component is a function that returns a class that renders the component that it passed to it:
import React from 'react'
function HigherOrderComponent(WrappedComponent) {
return class Enhancer extends React.Component {
constructor(props) {
super(props)
}
exampleFunc() {
// Code here
}
render() {
return <WrappedComponent exampleprop={ this.exampleFunc } />
}
}
}
export default HigherOrderComponent
Then you can import HigherOrderComponent into your stateless dumb component file and wrap the export:
import React from 'react'
import HigherOrderComponent from './HigherOrderComponent'
const DumbComponent = (props) => {
// Return JSX
// You can use props.exampleprop
}
export default HigherOrderComponent(DumbComponent)
Here are some articles that you can read on higher order components:
https://facebook.github.io/react/docs/higher-order-components.html
https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e

Closures in React

Is it ok use closures in react, for event handlers?
For example, i have some function and a lot of menu in navigation
and in navigation component i use something like this:
handleMenuClick(path) {
return () => router.goTo(path)
}
...
<MenuItem
handleTouchTap={this.handleMenuClick('/home')}
>
or i should prefer just arrow function?
<MenuItem
handleTouchTap={() => router.goTo('/home')}
>
first variant really make code cleaner, but i'm worried about performance with a large number of such elements
Both should be avoided.
While they'll both work, they both have the same weakness that they'll cause unnecessary renders because the function is being created dynamically, and will thus present as a different object.
Instead of either of those, you want to create your functions in a static way and then pass them in. For something like your MenuItem, it should just get the string for the path and then have the code to do the routing inside. If it needs the router, you should pass that in instead.
The function should then be a pre-bind-ed function (usually in the constructor) and just passed in.
export class MenuItem extends React.Component {
constructor() {
this.handleClick = () => this.props.router.go(this.props.path);
}
render() {
return (
<Button onClick={ this.handleClick }>Go to link</Button>
);
}
}
You can use an arrow function in the constructor. That way it isn't recreated every render function, and thus you avoid unnecessary renders. That pattern works well for single-line simple functions. For more complex functions, you can also create them as a separate function, then bind it in the constructor.
export class MenuItem extends React.Component {
handleClick() {
this.props.router.go(this.props.path);
}
constructor() {
this.handleClick = this.handleClick.bind(this);
}
render() { /* same as above */ }
}
The point of this is that the handler is the same function every time. If it was different (which both methods you describe above would be), then React would do unnecessary re-renders of the object because it would be a different function every time.
Here are two articles which go into more details:
https://ryanfunduk.com/articles/never-bind-in-render/
https://daveceddia.com/avoid-bind-when-passing-props/
when you define a new method inside a react component (Object) as we know functions are object in javascript.
let reactComponent={
addition: function(){ //some task ...},
render: function(){},
componentWillMount : function(){},
}
so, every new method should be bind with in the object using bind, but render() is already defined so we don't do
this.render = this.render.bind(this)
for each new function, except react lifecycle methods are needed to be added and hence, we call the object (constructor function) methods using this.method().

Trouble accessing props in callback function of Google Login using react-google-login

I'm using react-google-login in my react-redux project and having trouble accessing the props for the component in which this login button exists. I used react-facebook-login in a similar way and it works fine - however, console.log(this) in the loginGoogle() function prints 'undefined' whereas it printed the Javascript object representing the whole Login component in my similar loginFacebook() method. Any ideas as to how I can access this.props in loginGoogle()?
In my Login component:
//all needed import statements
class Login extends Component {
loginGoogle(response) {
console.log(response);
this.props.loginGoogleRequest(response.profileObj.email, response.accessToken, response.tokenObj.expires_in)
}
render() {
<GoogleLogin
clientId="{client id here}"
onSuccess={this.loginGoogle}
className="custom-google-btn"
/>
}
function mapDispatchToProps(dispatch) {
return {
loginGoogleRequest: (email, accessToken, expiresIn) => {
//some code that isn't being reached
}
}
}
export default connect(mapDispatchToProps)(Login);
I trimmed a lot of the fat out of this class in order to make it more readable - please let me know if it would help if I included more code in any way.
Try change loginGoogle definition to Arrow function:
loginGoogle(response) { => loginGoogle = (response) => {
An arrow function does not create its own "this", the this value of the enclosing execution context is used.
Or you may bind loginGoogle method, refer to this answer:
Why JSX props should not use arrow functions

Stubbing class field-functions in ES7

In my test suite, how can I stub a class' property, which is a function*? With normal methods it's easy using Object.getOwnPropertyNames(component.prototype) and monkey patching each found method, but after a long time of struggle I haven't found any way to extract the functions created by assigning to class' fields.
My testing stack consists of Jest with Jasmine2 and babel.
The problem with transpiling is that the arrow-function-properties are (as expected, of course) assigned to instance of the output transpiled "class" (function actually, of course). So I don't see any way of stubbing them other then instantiating this object, am I right? Here is the example of input es7 code and the babel's output. However I don't particularly like this solution, seems very hacky. The other drawback of this solution is that I don't get to directly instantiate the component's class.
(*) The background of this question is unit testing React components written in es7-like classes with arrow functions assigned to class' properties for the purpose of auto binding.
I was having the same problem, when writing unit tests for a project I'm working, and I think I got a good pattern to solve it. Hopefully it helps:
Context
Here is an example of a React component that has a method handleClick defined using the fat arrow notation.
import React, { Component } from 'react';
class Foo extends Component {
componentWillMount() {
this.handleClick();
}
handleClick = (evt) => {
// code to handle click event...
}
render() {
return (
<a href="#" onClick={this.handleClick}>some foo link</a>
);
}
}
Problem
As described in this link Babel will transpile the code so that the handleClick method is only available after instantiation (check lines 31 to 33 of the generated constructor function)
The problem here is that sometimes you need to have access to methods defined using the fat arrow notation before instantiating the class.
Lets say for example that you are writing unit tests for the componentWillMount class method and you want to stub the handleClick so that you only test the desired unit. But now you have a problem, since you can only have access to handleClick after instantiation and componentWillMount method will be called automatically by React as part of its instantiation lifecycle.
Solution
Here is how I can apply a simple pattern to solve problems like this:
import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import Foo from './foo';
describe('Foo', () => {
describe('componentWillMount method', () => {
const handleClickStub = sinon.stub();
class FooWrapper extends Foo {
constructor(props) {
super(props);
this.handleClick = handleClickStub;
}
}
it('should register a click event listener to the externalElement property', () => {
handleClickStub.reset();
mount(<FooWrapper />);
expect(handleClickStub.calledOnce).to.be.true;
});
});
});
Explanation
I've wrapped the original Foo component into a FooWrapper where on its constructor after initializing the original component I replace the original handleClick method with a stubbed version allowing me to property test my componentWillMount class.
Due to the way babel transpiles the arrow function syntax on class methods via transform-class-properties, the class method is no longer bound on the prototype, but rather the instance.
Using Jest 19's built-in assertion and .spyOn methods, this was my solution:
import React from 'react';
import { shallow } from 'enzyme';
describe('MyComponent', () => {
it('should spy properly', () => {
const wrapper = shallow(<Component />);
const wrapperInstance = wrapper.instance();
const spy = jest.spyOn(wrapperInstance, 'functionName');
wrapperInstance.functionName();
expect(spy).toHaveBeenCalledTimes(1);
})
});

Categories

Resources