DEV Community

Cover image for 5 Common Mistakes When Using OnEnable() and OnDisable() in Unity
Alok Krishali
Alok Krishali

Posted on

5 Common Mistakes When Using OnEnable() and OnDisable() in Unity

Unity's OnEnable() and OnDisable() methods look simple, but they can cause some surprisingly subtle bugs.

They are commonly used to subscribe and unsubscribe from events, start or stop listeners, refresh UI, and manage temporary behaviour. The catch is that these methods are tied to a component's enabled state, not simply to its creation or destruction.

Understanding exactly when they run can save you from duplicate event calls, memory leaks, unexpected behaviour, and null-reference exceptions.

Let's look at five common mistakes and how to avoid them.


1. Forgetting to Unsubscribe from Events

One of the most common patterns is subscribing to an event in OnEnable():

private void OnEnable()
{
    GameManager.OnGameOver += HandleGameOver;
}
Enter fullscreen mode Exit fullscreen mode

But if you subscribe without unsubscribing, the event publisher can continue holding a reference to your component.

The matching cleanup should happen in OnDisable():

private void OnEnable()
{
    GameManager.OnGameOver += HandleGameOver;
}

private void OnDisable()
{
    GameManager.OnGameOver -= HandleGameOver;
}

private void HandleGameOver()
{
    Debug.Log("Game Over!");
}
Enter fullscreen mode Exit fullscreen mode

Why does this matter?

Imagine a UI panel that can be enabled and disabled multiple times. If every OnEnable() adds another subscription, the same callback may eventually be invoked multiple times.

Even worse, if the publisher lives longer than the subscriber, failing to unsubscribe can keep references around longer than expected.

A useful rule

If you subscribe in OnEnable(), the safest default is:

Unsubscribe from the same event in OnDisable().

This keeps the subscription tied to the component's active state.


2. Assuming OnEnable() Runs Only Once

OnEnable() does not mean "run once when this object is created."

It runs whenever the component becomes enabled and active.

For example:

private void OnEnable()
{
    Debug.Log("Enabled!");
}
Enter fullscreen mode Exit fullscreen mode

If you do this:

gameObject.SetActive(false);
gameObject.SetActive(true);

gameObject.SetActive(false);
gameObject.SetActive(true);
Enter fullscreen mode Exit fullscreen mode

OnEnable() can run each time the object becomes active again.

The same principle applies when changing a component's enabled property:

myComponent.enabled = false;
myComponent.enabled = true;
Enter fullscreen mode Exit fullscreen mode

This can trigger OnDisable() and OnEnable().

Why is this important?

Code like this can become problematic:

private void OnEnable()
{
    LoadData();
    CreateSomething();
    StartListening();
}
Enter fullscreen mode Exit fullscreen mode

If you expect all of this to happen only once, disabling and re-enabling the component may repeat those operations.

For one-time initialization, consider using Awake() or Start() when appropriate.

For example:

private void Awake()
{
    Initialize();
}

private void OnEnable()
{
    StartListening();
}

private void OnDisable()
{
    StopListening();
}
Enter fullscreen mode Exit fullscreen mode

The important distinction is:

  • Awake() → initialization
  • Start() → initialization that can wait until the first frame
  • OnEnable() → happens whenever the component becomes enabled and active
  • OnDisable() → happens whenever the component becomes disabled or inactive

3. Confusing Disabled Components with Destroyed Objects

Disabling an object does not destroy it.

For example:

gameObject.SetActive(false);
Enter fullscreen mode Exit fullscreen mode

The GameObject still exists. Its components still exist. Its fields still contain their values.

The component simply isn't active.

That means this:

private void OnDisable()
{
    Debug.Log("Object disabled");
}
Enter fullscreen mode Exit fullscreen mode

does not mean:

"This object is being destroyed."

It means:

"This component is no longer enabled and active."

An object can later become active again:

gameObject.SetActive(true);
Enter fullscreen mode Exit fullscreen mode

and OnEnable() will run again.

Why does this distinction matter?

Suppose you clear important state in OnDisable():

private void OnDisable()
{
    currentTarget = null;
}
Enter fullscreen mode Exit fullscreen mode

You might assume the object is going away permanently.

But if another system re-enables it, the component now has to deal with that cleared state.

For cleanup that should happen specifically when an object is destroyed, OnDestroy() may be more appropriate.

private void OnDestroy()
{
    // Final cleanup before the object is destroyed.
}
Enter fullscreen mode Exit fullscreen mode

The lifecycle methods serve different purposes. Don't treat OnDisable() as a synonym for OnDestroy().


4. Unexpected Repeated Subscriptions

This mistake is closely related to the first one, but it deserves separate attention.

Consider:

private void OnEnable()
{
    EventManager.OnScoreChanged += UpdateScore;
}
Enter fullscreen mode Exit fullscreen mode

What happens if you forget the corresponding unsubscribe?

Every enable adds another subscription:

Enable → Subscribe
Disable
Enable → Subscribe again
Disable
Enable → Subscribe again
Enter fullscreen mode Exit fullscreen mode

Eventually, one event can produce multiple calls:

OnScoreChanged
    ↓
UpdateScore()
UpdateScore()
UpdateScore()
Enter fullscreen mode Exit fullscreen mode

This can lead to confusing bugs such as:

  • UI updating multiple times
  • Audio playing repeatedly
  • A method being executed several times
  • Performance gradually getting worse
  • State changing unexpectedly

The basic fix is:

private void OnEnable()
{
    EventManager.OnScoreChanged += UpdateScore;
}

private void OnDisable()
{
    EventManager.OnScoreChanged -= UpdateScore;
}
Enter fullscreen mode Exit fullscreen mode

But what if the event is only supposed to be subscribed once?

Then OnEnable() may not be the right place.

For example:

private void Awake()
{
    EventManager.OnScoreChanged += UpdateScore;
}

private void OnDestroy()
{
    EventManager.OnScoreChanged -= UpdateScore;
}
Enter fullscreen mode Exit fullscreen mode

This pattern ties the subscription to the object's lifetime rather than its enabled state.

The right approach depends on what you want:

Subscribe while enabled?

[OnEnable  ](https://learngamestutorial.com/unity-onenable-function-us/)→ Subscribe
OnDisable → Unsubscribe
Enter fullscreen mode Exit fullscreen mode

Subscribe for the object's lifetime?

Awake     → Subscribe
OnDestroy → Unsubscribe
Enter fullscreen mode Exit fullscreen mode

Choose the lifecycle that matches the actual requirement.


5. Null-Reference Problems

OnEnable() can run earlier than you expect, especially when a dependency hasn't been initialized yet.

For example:

[SerializeField]
private UIManager uiManager;

private void OnEnable()
{
    uiManager.Refresh();
}
Enter fullscreen mode Exit fullscreen mode

If uiManager hasn't been assigned, this can result in a NullReferenceException.

The same problem can happen when an object referenced by the component has already been destroyed or isn't available during a particular lifecycle transition.

Avoid assuming dependencies always exist

If a reference is required, make that requirement explicit.

For example:

[SerializeField]
private UIManager uiManager;

private void Awake()
{
    if (uiManager == null)
    {
        Debug.LogError("UIManager is not assigned.", this);
    }
}
Enter fullscreen mode Exit fullscreen mode

Then keep OnEnable() focused on the work that should happen when the component becomes active:

private void OnEnable()
{
    if (uiManager != null)
    {
        uiManager.Refresh();
    }
}
Enter fullscreen mode Exit fullscreen mode

However, don't blindly add null checks everywhere. A null check can hide an actual setup problem.

If a dependency is mandatory, failing loudly during initialization can make the bug much easier to find.


A Practical Pattern

For many event-driven components, this is a good starting pattern:

public class ScoreDisplay : MonoBehaviour
{
    [SerializeField]
    private ScoreManager scoreManager;

    private void Awake()
    {
        if (scoreManager == null)
        {
            Debug.LogError("ScoreManager is not assigned.", this);
        }
    }

    private void OnEnable()
    {
        if (scoreManager == null)
            return;

        scoreManager.OnScoreChanged += UpdateScore;
    }

    private void OnDisable()
    {
        if (scoreManager == null)
            return;

        scoreManager.OnScoreChanged -= UpdateScore;
    }

    private void UpdateScore(int score)
    {
        Debug.Log($"Score: {score}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, initialization happens in Awake(), while the event subscription follows the component's enabled state.

When the component is disabled, it stops listening. When it becomes enabled again, it subscribes again.


The Key Takeaway

OnEnable() and OnDisable() are primarily state-transition callbacks, not one-time initialization and destruction callbacks.

When working with them, ask yourself:

  1. Can this component be enabled more than once?
  2. Does every subscription have a matching unsubscribe?
  3. Do I need cleanup on disable, or only on destruction?
  4. Could this dependency be null when OnEnable() runs?
  5. Should this code run every time the component becomes active?

If you keep those questions in mind, many of the most common lifecycle bugs in Unity become much easier to prevent.

A simple mental model is:

Awake
  ↓
Start
  ↓
OnEnable ←────────────┐
  ↓                   │
Component is active   │
  ↓                   │
OnDisable ────────────┘
  ↓
OnDestroy
Enter fullscreen mode Exit fullscreen mode

Treat OnEnable() and OnDisable() as a pair whenever you're managing temporary subscriptions or listeners, and your components will be much easier to reason about.

If you like the article, Please like and share your valuable feedback. If something is not clear feel free to ask in comment.

Top comments (0)