smithery/vallafederico

solid-core-lifecycle

SolidJS lifecycle: onMount for DOM access after mount, onCleanup for resource disposal, effect lifecycle management, component vs effect cleanup.

Installation

$ npx skills add smithery/vallafederico --skill solid-core-lifecycle

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/vallafederico · top by installs.

npx skills add smithery/vallafederico

Browse all from smithery/vallafederico

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

More metadata
globs
["**\/*lifecycle*","**\/*mount*","**\/*cleanup*"]

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,283 B
  • docs SUMMARY.md 173 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

SolidJS Lifecycle Management

Complete guide to managing component and effect lifecycle in SolidJS. Understanding lifecycle is essential for proper resource cleanup and avoiding memory leaks.

onMount - Component Mounting

onMount runs code after components mount to the DOM. Perfect for accessing refs, setting up third-party libraries, and one-time initializations.

import { onMount } from "solid-js";

function MyComponent() {
  let ref: HTMLButtonElement;

  // Runs after component mounts to DOM
  onMount(() => {
    ref.disabled = true;
    // Access DOM elements safely
    // Initialize third-party libraries
    // Set up one-time event listeners
  });

  return <button ref={ref}>Focus me!</button>;
}

Key characteristics:

  • Runs once after initial render
  • Non-tracking (equivalent to createEffect with no dependencies)
  • Perfect for DOM access via refs
  • Ideal for third-party library initialization

Use cases:

  • Accessing DOM refs
  • Initializing libraries (charts, maps, etc.)
  • Setting up one-time event listeners
  • Fetching initial data

onCleanup - Resource Disposal

onCleanup registers cleanup methods that execute when components unmount or tracking scopes dispose. Essential for preventing memory leaks.

import { createSignal, onCleanup } from "solid-js";

function Component() {
  const [count, setCount] = createSignal(0);

  const handleClick = () => setCount((value) => value + 1);

  // Add event listener
  document.addEventListener("click", handleClick);

  // Cleanup when component unmounts
  onCleanup(() => {
    document.removeEventListener("click", handleClick);
  });

  return <main>Document has been clicked {count()} times</main>;
}

When cleanup runs:

  • In components: When component unmounts
  • In effects: When effect is disposed or recalculated
  • In memos: When memo is disposed
  • In roots: When root is disposed

Effect Lifecycle

Effects have their own lifecycle. Cleanup runs before effect re-executes:

import { createEffect, onCleanup, createSignal } from "solid-js";

function Component() {
  const [id, setId] = createSignal(1);

  createEffect(() => {
    const currentId = id();
    
    // Setup
    const subscription = subscribeToData(currentId);
    
    // Cleanup runs:
    // 1. Before effect re-executes (when id changes)
    // 2. When effect is disposed (component unmounts)
    onCleanup(() => {
      subscription.unsubscribe();
    });
  });

  return <button onClick={() => setId(id() + 1)}>Next</button>;
}

Cleanup execution order:

  1. Previous cleanup runs (if effect re-executes)
  2. Effect body runs
  3. New cleanup registered

Component Lifecycle vs Effect Lifecycle

Component lifecycle:

  • Component function runs once on initialization
  • onMount runs after DOM mount
  • onCleanup runs on unmount
  • No re-execution on state changes (fine-grained reactivity)

Effect lifecycle:

  • Effect runs initially, then on dependency changes
  • Cleanup runs before re-execution
  • Cleanup runs on disposal
function Component() {
  const [count, setCount] = createSignal(0);

  // Component body runs ONCE
  console.log("Component initialized");

  // Runs after mount
  onMount(() => {
    console.log("Component mounted");
  });

  // Runs on unmount
  onCleanup(() => {
    console.log("Component cleanup");
  });

  // Effect runs when count changes
  createEffect(() => {
    console.log("Effect runs:", count());
    
    onCleanup(() => {
      console.log("Effect cleanup");
    });
  });

  return <button onClick={() => setCount(count() + 1)}>Count: {count()}</button>;
}

Cleanup Best Practices

1. Always Clean Up Event Listeners

function Component() {
  const handleResize = () => {
    // Handle resize
  };

  window.addEventListener("resize", handleResize);
  
  onCleanup(() => {
    window.removeEventListener("resize", handleResize);
  });
}

2. Clean Up Subscriptions

function Component() {
  const [data, setData] = createSignal(null);

  createEffect(() => {
    const subscription = observable.subscribe((value) => {
      setData(value);
    });

    onCleanup(() => {
      subscription.unsubscribe();
    });
  });
}

3. Clean Up Intervals/Timeouts

function Component() {
  createEffect(() => {
    const interval = setInterval(() => {
      // Do something
    }, 1000);

    onCleanup(() => {
      clearInterval(interval);
    });
  });
}

4. Clean Up AbortControllers

function Component() {
  createEffect(() => {
    const controller = new AbortController();

    fetch("/api/data", { signal: controller.signal })
      .then((res) => res.json())
      .then(setData);

    onCleanup(() => {
      controller.abort();
    });
  });
}

5. Multiple Cleanups

You can register multiple cleanups - they all run in reverse order:

function Component() {
  onMount(() => {
    const listener1 = setupListener1();
    const listener2 = setupListener2();

    onCleanup(() => {
      listener2.cleanup();
    });

    onCleanup(() => {
      listener1.cleanup();
    });
    // Cleanup order: listener1, then listener2 (reverse registration)
  });
}

Common Patterns

Accessing Refs After Mount

function Component() {
  let inputRef: HTMLInputElement;

  onMount(() => {
    // Safe to access ref after mount
    inputRef.focus();
    inputRef.select();
  });

  return <input ref={inputRef} />;
}

Third-Party Library Initialization

function ChartComponent() {
  let chartContainer: HTMLDivElement;
  let chartInstance: Chart;

  onMount(() => {
    // Initialize chart library
    chartInstance = new Chart(chartContainer, {
      // config
    });
  });

  onCleanup(() => {
    // Cleanup chart instance
    if (chartInstance) {
      chartInstance.destroy();
    }
  });

  return <div ref={chartContainer} />;
}

Conditional Cleanup

function Component() {
  createEffect(() => {
    let cleanup: (() => void) | undefined;

    if (someCondition()) {
      cleanup = setupSomething();
    }

    onCleanup(() => {
      if (cleanup) {
        cleanup();
      }
    });
  });
}

Async Operations Cleanup

function Component() {
  createEffect(() => {
    let cancelled = false;

    fetchData().then((data) => {
      if (!cancelled) {
        setData(data);
      }
    });

    onCleanup(() => {
      cancelled = true;
    });
  });
}

Anti-Patterns to Avoid

❌ Don't Access Refs Outside onMount

// ❌ Wrong - ref may not be attached yet
function Component() {
  let ref: HTMLDivElement;
  ref.focus(); // Error!
  return <div ref={ref} />;
}

// ✅ Correct
function Component() {
  let ref: HTMLDivElement;
  onMount(() => {
    ref.focus(); // Safe
  });
  return <div ref={ref} />;
}

❌ Don't Forget to Clean Up

// ❌ Wrong - memory leak
function Component() {
  document.addEventListener("click", handleClick);
  // No cleanup!
}

// ✅ Correct
function Component() {
  document.addEventListener("click", handleClick);
  onCleanup(() => {
    document.removeEventListener("click", handleClick);
  });
}

❌ Don't Use onMount for Reactive Code

// ❌ Wrong - onMount doesn't track
function Component() {
  const [count, setCount] = createSignal(0);
  
  onMount(() => {
    // This only runs once, won't react to count changes
    console.log(count());
  });
}

// ✅ Correct - use createEffect
function Component() {
  const [count, setCount] = createSignal(0);
  
  createEffect(() => {
    // This tracks count and runs when it changes
    console.log(count());
  });
}

Summary

  • onMount: One-time setup after DOM mount
  • onCleanup: Resource disposal on unmount/disposal
  • Effect cleanup: Runs before effect re-executes
  • Always clean up: Event listeners, subscriptions, intervals, timeouts
  • Component runs once: Use effects for reactive code, not component body