React Question: How come some methods require binding and some don't?

Hi all,

I am just wrapping up my drum-machine version 1, and I realized that only some of the methods I defined requires the bind.

I read a couple of posts here about bind (like this one), and I think I get it, but I don’t know why it isn’t always needed.

For example, take a look at my DrumMachine component below. You’ll see that the constructor has the line this.onKeyPress = this.onKeyPress.bind(this);, but no other methods require this for some reason.

Why is this the case?

Thanks in advance, y’all!

class DrumMachine extends React.Component {
	
	constructor(props) {
		super(props);
		this.state = {
			clipName: ""
		};
               this.onKeyPress = this.onKeyPress.bind(this);
	}

	playSound(elem_id) {
		document.getElementById(elem_id).play();
		let clip;
		for (let audio of audioList) {
			if (audio.key === elem_id) {
				clip = audio.name;
			}
		}
		this.setState({
			clipName: clip
		}, ()=>console.log("Just played: "+ this.state.clipName))
	}

	onKeyPress(e) {
		let pressed = String.fromCharCode(e.keyCode);
		if (validKeys.includes(pressed)) { 
			this.playSound(pressed);
		} else {
			console.log(pressed + " is not a valid key.");
		}
	}

	componentDidMount() {
		document.addEventListener("keydown", this.onKeyPress);
	}
	
	componentWillUnmount() {
		document.removeEventListener("keydown", this.onKeyPress);
	}

	handleClick(elem_id) {
		this.playSound(elem_id);	
		// console.log("I clicked on " + elem_id);
	}

	render() {
		return (
			<div id="drum-machine">
				<Display clipName={this.state.clipName}/>
				<Pads handleClick={(elem_id)=>this.handleClick(elem_id)}/>
			</div>
		);
	}
}

Link to my codePen: https://codepen.io/kiwisquash/pen/OeOzKW

For the record, that codepen does not contain this code.

That said, if you don’t bind the onKeyPress event to your DrumMachine component, when the keyPress event is triggered, it will trigger in the context of the DOM node, rather than in the context of the DrumMachine. By binding your onKeyPress handler to the current context, then the this keyword, inside your keyPress handler, remains the current DrumMachine instance.

1 Like

For the record, that codepen does not contain this code.

Yeah, I uh… forgot to hit the “save” button before sharing the link. Sorry about that.

That said, if you don’t bind the onKeyPress event to your DrumMachine component, when the keyPress event is triggered, it will trigger in the context of the DOM node, rather than in the context of the DrumMachine. By binding your onKeyPress handler to the current context, then the this keyword, inside your keyPress handler, remains the current DrumMachine instance.

That makes sense. Thanks for the explanation.

But how come I don’t need to bind handleClick or playSound? I feel like I am missing something…

Take a look at your code - where are you attaching those keyUp/keyDown events? Are they attached to your DrumMachine, or to its rendered component? Or are they, in fact, attached… somewhere else?

1 Like

Ah, they are attached to … document?

Got it. I think I understand now. Thank you very much for your help.

this.onKeyPress = this.onKeyPress.bind(this);

@kiwisquash Just one more note. The above line would not be necessary if you were to declare your onKeyPress method using arrow function syntax (like below):

	onKeyPress = (e) => {
		let pressed = String.fromCharCode(e.keyCode);
		if (validKeys.includes(pressed)) { 
			this.playSound(pressed);
		} else {
			console.log(pressed + " is not a valid key.");
		}
	}

Why? Because arrow functions do not have their own this, so this ends up referencing the DrumMachine component which is what the method was called upon.

3 Likes

It has to do with the way “this” works in JS. “this” is not determined by its location in the code but in what context it is called. Kyle Simpson simply refers to this as “call site”. bind() binds the function to the class no matter what context you end up running it in. It becomes very important for React due to the need to pass functions as props in the same way you do properties of a class.

You have 2 options. You can use a regular method syntax and bind it in the constructor or you can use an arrow function. Arrow functions have no this binding what-sover.

1 Like

Thank you @snowmonkey, @RandellDawson, and @JasonBBelcher for taking your time to share your JS wisdom. I feel like I have a much better understanding of this.

Wishing y’all a happy Wednesday!

1 Like

No problem! Happy to help!