Explain Codes LogoExplain Codes Logo

React onClick function fires on render

javascript
event-handling
performance-optimization
react-hooks
Alex KataevbyAlex Kataev·Mar 4, 2025
TLDR

Prevent spontaneous function execution during render by ensuring onClick receives a function reference, not a function call. Use an arrow function for inline handling or pass the callback directly if there are no required parameters.

// Uh-oh: It's firing as soon as it sees daylight! <button onClick={ringTheBell()} /> // Good bell: It's patient. Waits until clicked. <button onClick={() => ringTheBell()} /> // Good bell with no arguments: Simplicity at its best. <button onClick={ringTheBell} />

Follow for more details, understanding the difference between function references and function calls can save you from unnecessary debugging.

Handling parameters in onClick

To handle parameters with onClick, turn to our friends: arrow functions or the .bind method. The choice is yours, but keep a mindful eye on performance:

Arrow function:

<button onClick={() => tossTheRing(intoTheVolcano)} />

Bound function:

<button onClick={tossTheRing.bind(this, intoTheVolcano)} />

Beware: .bind in render behaves like a spoiled child, demanding a new function every time render is called. Arrow functions are the polite alternative in such situations.

Performance considerations and potential pitfalls

To avoid unnecessary rerendering, define callback functions outside of render in class components or out of the functional component body. Here is a 'good citizen' example using hooks to ensure a stable function reference:

function Task({taskId, taskName}) { // Behold the power of 'useCallback'! const removeTask = useCallback(() => { dontForgetToDelete(taskId); }, [taskId]); return <button onClick={removeTask}>Delete {taskName}</button>; }

Stay alert for these common traps:

  • Defining function inside render without using useCallback, causing a new function on each render.
  • Using onClick={makeCoffee()} instead of onClick={() => makeCoffee()}, causing coffee to spill on render.

Taming this in class components

In class components, taming this is essential. Here are some patterns to keep everyone happy:

Class properties + arrow function:

class MyButton extends React.Component { handleClick = () => { // just a click and... console.log('World peace achieved!'); }; render() { return <button onClick={this.handleClick}>Click Me</button>; } }

Binding in the constructor:

class MyButton extends React.Component { constructor(props) { super(props); // Now bind it, brother! this.handleClick = this.handleClick.bind(this); } handleClick() { console.log('Also world peace, but in a vintage way.'); } render() { return <button onClick={this.handleClick}>Click Me</button>; } }

Both methods tame this effectively but arrow functions are modern and avoid pesky performance issues of binding in each render.

Keep your code clean and meaningful

Clean, meaningful code is a joy to read:

  • Move out complex event handlers from inline definitions. It's okay to let your render breathe.
  • Use Hooks API and utility functions where code is repeated across different handlers and components.
  • Remember, buttons are for everyone. Make event handlers accessible by adding proper aria- attributes.