Reputation: 441
I am binding function in two ways, the first one is working fine, but its a bad approach to use es6 function syntax inside return method as it calls a new method on every instance, all the code is working fine, i just need help with binding function. Binding in return statement works fine.
import React, { Component } from 'react';
import './ColorBox.css';
export default class ColorBox extends Component{
//Constructor
constructor(props){
super(props);
this.state = { copied: false};
}
// Not binded because its called inside the binded function, althrough it doesnot matter
changeCopyState(){
this.setState({ copied: true}, () => (
setTimeout(() => (
this.setState({copied: false})
), 1500)
))
}
// Function to copy color code to clipboard
copyToClipBoard(str) {
const element = document.createElement('textarea');
element.value = str;
document.body.appendChild(element);//insert textarea into html body
element.select();
document.execCommand('copy');//internal command to copy from text area
document.body.removeChild(element);//remove after coping command
this.changeCopyState();
}
render(){
let {name, background} = this.props;
let {copied} = this.state;
return (
<div style={{ background}} className="ColorBox">
<div style={{ background }} className={`copy-overlay ${ copied && "show"}`} />
<div className="box-content">
<span>{name}</span>
</div>
<button className="copy-button" onClick={() => this.copyToClipBoard(background)}>Copy</button>
<span className="more-shades">More</span>
</div>
)
}
}
now if i try to bind the function inside the constructor, it will give me an error of exceeding limit on calling function etc, why is this behaviour occurring.
import React, { Component } from 'react';
import './ColorBox.css';
export default class ColorBox extends Component{
//Constructor
constructor(props){
super(props);
this.state = { copied: false};
this.copyToClipBoard = this.copyToClipBoard.bind(this);
}
// Not binded because its called inside the binded function, althrough it doesnot matter
changeCopyState(){
this.setState({ copied: true}, () => (
setTimeout(() => (
this.setState({copied: false})
), 1500)
))
}
// Function to copy color code to clipboard
copyToClipBoard(str) {
const element = document.createElement('textarea');
element.value = str;
document.body.appendChild(element);//insert textarea into html body
element.select();
document.execCommand('copy');//internal command to copy from text area
document.body.removeChild(element);//remove after coping command
this.changeCopyState();
}
render(){
let {name, background} = this.props;
let {copied} = this.state;
return (
<div style={{ background}} className="ColorBox">
<div style={{ background }} className={`copy-overlay ${ copied && "show"}`} />
<div className="box-content">
<span>{name}</span>
</div>
<button className="copy-button" onClick={this.copyToClipBoard(background)}>Copy</button>
<span className="more-shades">More</span>
</div>
)
}
}
Upvotes: 1
Views: 778
Reputation: 203427
onClick={this.copyToClipBoard(background)}
invokes the callback immediately, thus causing the render looping you see.
Convert copyToClipBoard
to a curried function so it consumes a background
argument, closed over in scope, and returns a function to be used as the callback.
// Function to copy color code to clipboard
copyToClipBoard(str) {
return () => {
const element = document.createElement('textarea');
element.value = str;
document.body.appendChild(element); //insert textarea into html body
element.select();
document.execCommand('copy'); //internal command to copy from text area
document.body.removeChild(element); //remove after coping command
this.changeCopyState();
};
}
Usage:
<button
className="copy-button"
onClick={this.copyToClipBoard(background)} // <-- returns callback handler
>
Copy
</button>
Convert copyToClipBoard
to a curried arrow function so this
is bound automatically and you don't need the binding in the constructor
.
// Function to copy color code to clipboard
copyToClipBoard = (str) => () => {
const element = document.createElement('textarea');
element.value = str;
document.body.appendChild(element); //insert textarea into html body
element.select();
document.execCommand('copy'); //internal command to copy from text area
document.body.removeChild(element); //remove after coping command
this.changeCopyState();
};
Usage:
<button
className="copy-button"
onClick={this.copyToClipBoard(background)} // <-- returns callback handler
>
Copy
</button>
background
is available already in props
, just consume it from there.
// Function to copy color code to clipboard
copyToClipBoard(str) {
const { background } = this.props; // <-- destructure from props
const element = document.createElement('textarea');
element.value = background; // <-- use background
document.body.appendChild(element); //insert textarea into html body
element.select();
document.execCommand('copy'); //internal command to copy from text area
document.body.removeChild(element); //remove after coping command
this.changeCopyState();
}
Usage:
<button
className="copy-button"
onClick={this.copyToClipBoard} // <-- just attach handler
>
Copy
</button>
Upvotes: 2
Reputation: 512
It's probably because of a circular reference. You're accessing something which is accessing something, resulting in an infinite amount of calls and causing the call stack to exceed its limit.
You could instead just do the binding inside of the onClick method:
<button className="copy-button" onClick={this.copyToClipBoard.bind(this, background)}>Copy</button>
Upvotes: 0