Avoiding inline functions in React: How to bind functions with arguments? - javascript

I'm trying to follow the no-bind rule for React using the pattern that they have recommended with ES6 classes:
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
_onClick() {
// Do whatever you like, referencing "this" as appropriate
}
}
However, when I need to pass arguments in to _onClick, what needs to change?
I've tried something like:
import {someFunc} from 'some/path';
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this, a, b);
}
render() {
const {
prop1,
prop2
} = this.props;
return (
<div onClick={this._onClick(prop1, prop2}>
Hello!
</div>
);
}
_onClick = (a, b) => {
someFunc(a, b);
}
}
However, this does not work. What needs to be altered?

The call to bind in the constructor should only pass this as a single argument.
this._onClick = this._onClick.bind(this);
Here you are overwriting the property this._onClick with a new one that has the correct this bound. If your function takes two arguments, then you should pass those as normal at call time.
Passing additional arguments to bind means that the function returned already has those arguments supplied - in your attempt the _onClick function will always have its first two arguments undefined, as a and b have no value in the constructor.
Now that you have bound this to your function, you can access this.props from within there, rather than having to pass arguments:
import {someFunc} from 'some/path';
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
_onClick() {
const {
prop1,
prop2
} = this.props;
someFunc(prop1, prop2);
}
}

You should use partial application. Basically you initialise your onClick function with the parameters you want, and the onClick function will return a new function to be called when the div is clicked.
import {someFunc} from 'some/path';
class Foo extends React.Component {
render() {
return (
<div onClick={this._onClick(prop1, prop2)}>
Hello!
</div>
);
}
_onClick = (a, b) => {
return () => someFunc(a, b);
}
}
PS: this only applies if your parameters a and b are not part of your this.props, if they are then you should just do as Tom Fenech said.

To answer your question there is nothing special you have to do in order to pass arguments to your this._onClick function.
the proper revised code will be:
class Foo extends React.Component {
constructor() {
super();
this._onClick = this._onClick.bind(this);
}
render() {
return (
<div onClick={() => this._onClick(1, 2)}>
Hello!
</div>
);
}
_onClick = (a, b) => {
console.log(a, b);
}
}
Secondly, the way you are calling this._onClick is not the right way to invoke a function on click.
Right now what is happening that on each render process your function is getting called because you didn't pass the function as an argument rather you invoked that function and assigned its returned value to the onClick prop.
you have to do this like:
render() {
return (
<div onClick={() => this._onClick(prop1, prop2)}>
Hello!
</div>
);
}
By Invoking your function this way you ensure the this._onClick will get called when click event occurs.

Another method is to use Babel stage 1 autobind and skip the constructor.
import {someFunc} from 'some/path';
class Foo extends React.Component {
_onClick = () => {
const {
prop1,
prop2
} = this.props;
someFunc(prop1, prop2);
}
render() {
return (
<div onClick={this._onClick}>
Hello!
</div>
);
}
}

Related

Accessing class property via this in an event call

I'm trying to acces a class property via 'this' in a callback function of an onClick event, but there is a scope-issue and the property isn't defined.
I've tried a couple of setups, but I think I'm just hitting myself in my confusion more than anything.
class MyClass extends React.Component {
constructor(props) {
super(props)
this.myProperty = "myValue"
}
onElementClicked = (event, other, parameters) => {
console.log(this.myProperty) //Undefined.
}
render() {
return (
<div>
<SubElement click={this.onElementClicked} />
</div>
)
}
}
function SubElement({ click }) {
const number = "Click me", other = null, parameters = null;
return (
<p>
<span onClick={e => click(e, other, parameters)}>
{number}
</span>
</p>
);
}
ReactDOM.render(
<MyClass />,
document.querySelector("main")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main>Please enable JS</main>
You should not be using class fields and arrow functions for methods as they are attached to the object instance instead of the class prototype, which defeats prototypal inheritance.
Replace the arrow function with a regular function with the method definition syntax. Also, bind the function to this before passing it as prop, otherwise it will be "detached" from the class:
export default class MyClass {
constructor() {
this.myProperty = "myValue"
this.onElementClicked = this.onElementClicked.bind(this);
}
onElementClicked(event, other, parameters) {
console.log(this.myProperty) // Works.
}
render() {
ReactDOM.render(
<div>
<subElement click={this.onElementClicked}/>
</div>
)}
}
}

Is there a way to access a function returned by a React component through a parent HTMLDivElement?

I have a custom React component, Foo, which contains another custom component, SubFoo, which in turn contains an inline library component, <Bar/>.From Foo I need to access a function, BarFunc, which is returned by <Bar/>. Unfortunately, <Bar/> is not set up to work with refs - when I try to pass in a ref to <Bar/> (forwarded from Foo to SubFoo using React.forwardRef()), I get undefined.
However, there is a div that is parent to <Bar/> in SubFoo, and if I pass my ref to this div I get back an HTMLDivElement.
Is there any way to execute BarFunc given my HTMLDivElement?
There are multiple ways to accomplish what you are looking to do:
Modify SubFoo to proxy the behavior that Bar provides via its render-prop. Then Foo can take a reference to SubFoo and invoke the behavior indirectly:
class SubFoo extends React.Component {
barBehavior = () => this.barBehaviorFunc?.()
renderProp = barApi => {
this.barBehaviorFunc = barApi.barBehavior;
return <div><other components={here} /></div>;
}
render() {
return <Bar>{this.renderProp}</Bar>;
}
}
class Foo extends React.Component {
onClick() { this.ref?.barBehavior();
render() {
return <SubFoo ref={r => this.ref = r} />
}
}
Use the React.Context API to expose this behavior upwards (if you have unmodifiable code in between Foo and SubFoo).
const BarBehaviorContext = React.createContext(() => {});
class Foo extends React.Component {
setBarBehavior = barBehavior => this.barBehavior = barBehavior;
render() { return <BarBehaviorContext.Provider value={this.setBarBehavior}><SubFoo /></BarBehaviorContext.Provider>;
}
class SubFoo extends React.Component {
render() {
return <BarBehaviorContext.Consumer>{setter => <Bar>{api => (setter(api.barBehavior), <div>...</div>)}</Bar>}</BarBehaviorContext.Consumer>
}
}

Use a parent function inside a child function

I'm trying to call a parent function with arguments from a child component but I'm not sure exactly how to get it working. I specifically need to be able to call the parent function from the child within another function, so I tried passing a reference to the function through props but this is not quite right. The parent class owns a resource that only it should interact with through the specific function call I'm passing. When done in the following way, I am told the function isn't defined.
export class ParentClass extends React.Component {
ParentFunctionWithArguments(a, b) {
alert("a is being used by my private resource");
alert("b is being used by my private resource");
return true; //some result based on a and b
}
render() {
return (
<ChildClass>ParentFunctionWithArguments={() => this.ParentFunctionWithArguments()}</ChildClass>
);
}
}
And
export class ChildClass extends React.Component {
...
handleOk = (e) => {
...
if (condition) {
if (this.props.ParentFunctionWithArguments(a, b)) {}
}
...
};
...
}
<childComponent callFromChild={this.parentMethod}/>
//in child component call like below
this.props.callFromChild()
You simply need to the apss the parent function as props to the Child component and not call it within the children
export class ParentClass extends React.Component {
ParentFunctionWithArguments(a, b) {
alert("a is being used by my private resource");
alert("b is being used by my private resource");
return true; //some result based on a and b
}
render() {
return (
<ChildClass ParentFunctionWithArguments={this.ParentFunctionWithArguments}></ChildClass>
);
}
}
and simply call it in child like
export class ChildClass extends React.Component {
handleOk = (e) => {
...
if (condition) {
if (this.props.ParentFunctionWithArguments(a, b)) {}
}
...
};
...
}
What I was looking for was the "bind" function
something like this in the constructor of the parent:
export class ParentClass extends React.component {
constructor() {
this.ParentFunctionWithArguments = this.ParentFunctionWithArguments.bind(this);
}
... //rest unchanged
}
This will allow the child to use the passed in parent function in vanilla react.

How to execute bound function outside of event handler in React?

I'm probably asking the wrong question, but I'd like to be able to execute a parent function when called from a child function, not an event.
I have more or the less the following setup: Declaring the _foo method in the parent and passing it on down to ChildTwo, where executing it via an onClick event handler works as expected. However, I ran into a situation where I need to call the _foo method manually from inside another method (I've simplified it here, but it will be called conditionally).
My question is what do I need to do to call the _foo method from _bar() ?
Thanks in advance!
export defaultclass Parent extends Component {
constructor() {
super();
}
_foo() {
alert('alert!');
}
render() { <ChildOne _foo={this._foo.bind(this)} /> }
}
const ChildOne = (props) => {
const { _foo } = props;
return ( <ChildTwo _foo={_foo} /> );
}
export default class ChildTwo extends Component {
constructor(props) {
super(props);
this._foo = this.props._foo.bind(this);
}
_bar() {
//this._foo.call();
//this._foo();
//what do I do here?
}
render() {
return (
<div>
<button onClick={this._foo}> Works! </button>
<button onClick={this._bar}>Doesnt Work!</button>
</div>
);
}
};
If you really want to do this, then I would solve it by passing the child component as an argument to the method that is still bound to the original parent.
For example:
export defaultclass Parent extends Component {
constructor() {
super();
this._foo = this._foo.bind(this)
}
_foo(childComponent) {
alert({ parent: this, child: childComponent });
}
render() { <ChildOne _foo={this._foo} /> }
}
const ChildOne = (props) => {
const { _foo } = props;
return ( <ChildTwo _foo={_foo} /> );
}
export default class ChildTwo extends Component {
constructor(props) {
super(props);
this._bar = this._bar.bind(this);
}
_bar() {
const { _foo } = this.props;
// Passing a reference to self as argument
_foo(this);
}
render() {
return (
<div>
<button onClick={this._bar}>Should Work Now!</button>
</div>
);
}
};

TestUtils.renderIntoDocument return null, if it declared as anonymous function

I have 2 variants of code:
FIRST (declare as class):
export default class COMPONENT_NAME extends React.Component{
constructor(props){
super(props);
this.props = props;
}
....
render = () => <div className="clName"></div>
}
SECOND (declare as anonymous function):
export default (props) => <div className="clName"></div>
JEST CODE:
jest.dontMock('../js/components/COMPONENT_NAME/index');
const COMPONENT_NAME = require('../js/components/COMPONENT_NAME/index.js').default;
var loaderComponent;
...
...
function renderComponent() {
loaderComponent = TestUtils.renderIntoDocument(
<COMPONENT_NAME />
);
}
Why test works only in the first case?
In the second case renderIntoDocument return null.
I can't find any information about it.
So the question is - does JEST support rendering anonymous functions?
try changing
jest.dontMock('../js/components/COMPONENT_NAME/index');
to
jest.unmock('../js/components/COMPONENT_NAME/index');
You should wrap your functional component into div when passing it to TestUtils.renderIntoDocument function:
function renderComponent() {
loaderComponent = TestUtils.renderIntoDocument(
<div>
<COMPONENT_NAME />
</div>
);
}

Categories