This error occurs when React tries to invoke an event handler but receives a non-function value instead. Common causes include immediately invoking functions in JSX, incorrect binding in class components, or passing undefined/null values to event props.
This error appears when you assign a non-function value to an event handler prop like onClick, onChange, or onSubmit. React expects these props to receive function references that can be called when the corresponding event occurs. The error typically manifests as "TypeError: X is not a function" where X might be the handler name, "undefined", or some other value. This happens because React tries to invoke the handler when the event fires, but the value isn't callable. In most cases, this is caused by invoking the function immediately in JSX (using parentheses), forgetting to bind this in class components, or passing the result of a function call instead of the function itself.
The most common mistake is adding parentheses to the function call in JSX, which executes it immediately instead of passing a reference.
Incorrect:
// This calls handleClick immediately and assigns its return value (undefined)
<button onClick={handleClick()}>Click me</button>Correct:
// This passes the function reference
<button onClick={handleClick}>Click me</button>If you need to pass arguments, wrap it in an arrow function:
<button onClick={() => handleClick(id)}>Click me</button>
// Or with the event object
<button onClick={(e) => handleClick(e, id)}>Click me</button>In class components, methods aren't automatically bound to the instance. You need to bind them explicitly.
Option 1: Bind in the constructor (recommended for class components)
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// this is correctly bound
console.log(this.state);
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}Option 2: Use class fields with arrow functions
class MyComponent extends React.Component {
handleClick = () => {
// Arrow functions automatically bind this
console.log(this.state);
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}Option 3: Use arrow function in JSX (can cause performance issues)
<button onClick={() => this.handleClick()}>Click</button>Check that the handler function is actually defined and spelled correctly.
function MyComponent() {
// Make sure the handler is defined
const handleClick = () => {
console.log('Clicked');
};
// Check for typos
return <button onClick={handleClick}>Click</button>; // ✓
// return <button onClick={handelClick}>Click</button>; // ✗ Typo
}If using props, verify the prop is being passed:
function ParentComponent() {
const handleAction = () => console.log('Action');
return <ChildComponent onAction={handleAction} />;
}
function ChildComponent({ onAction }) {
// Verify onAction exists before using
return <button onClick={onAction}>Do Action</button>;
}If event handlers are optional props, add checks to prevent calling undefined functions.
function MyComponent({ onClick }) {
const handleClick = (e) => {
// Check if onClick prop exists before calling
if (onClick && typeof onClick === 'function') {
onClick(e);
}
};
return <button onClick={handleClick}>Click me</button>;
}Or use optional chaining:
function MyComponent({ onClick }) {
return <button onClick={(e) => onClick?.(e)}>Click me</button>;
}Ensure you're correctly destructuring props and not losing handler references.
Incorrect:
function MyComponent(props) {
const { onClick } = props.handlers; // Wrong structure
return <button onClick={onClick}>Click</button>;
}Correct:
function MyComponent({ onClick }) {
return <button onClick={onClick}>Click</button>;
}
// When using
<MyComponent onClick={handleClick} />Also check that you're not accidentally overwriting handlers:
function MyComponent({ onClick }) {
// Don't reassign to a non-function
// const onClick = "something"; // ✗ This breaks it
return <button onClick={onClick}>Click</button>;
}Function vs. Function Call:
The distinction between onClick={myFunction} and onClick={myFunction()} is critical. The first passes a reference to the function, allowing React to call it later. The second immediately executes the function and passes its return value (usually undefined) to onClick.
Performance Considerations:
Creating new arrow functions in JSX (onClick={() => handleClick()}) creates a new function on every render. This can cause performance issues if the component renders frequently or if the prop is passed to PureComponent/React.memo components, as the new function reference breaks memoization.
TypeScript Event Types:
When using TypeScript, properly type your event handlers to catch errors at compile time:
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// Type-safe event handling
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// Type-safe input handling
};React Synthetic Events:
React wraps native browser events in SyntheticEvent objects. If you try to access the event asynchronously (like in a setTimeout), you need to call e.persist() in older React versions, or use e.nativeEvent to access the underlying browser event.
Modern React (Hooks) vs. Class Components:
Function components with hooks don't have the this binding issue that class components have. If you're starting new projects, prefer function components to avoid this entire class of errors.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite