← Back to Home

⚛️ React Hooks - Complete Guide

Master all 3 SignalForge hooks: useSignal, useSignalValue, and useSignalEffect

📚 What You'll Learn

  • useSignal() - Create reactive state (most common)
  • useSignalValue() - Read signals without setter (read-only)
  • useSignalEffect() - React to signal changes (side effects)
  • When to use each hook and best practices

🔍 Quick Reference

HookReturnsUse When
useSignal(val)[value, setValue]You need to read AND write
useSignalValue(sig)value (read-only)You only need to read
useSignalEffect(fn)voidRun side effects on changes

useSignal()

10
const [count, setCount] = useSignal(10);
setCount(count + 1) // update

Computed Value

0
Auto-computed value
(count * 2)
const [doubled, setDoubled] = useSignal(0);
useSignalEffect(() => {
  setDoubled(count * 2);
});

useSignalEffect()

useSignalEffect(() => {
  // Automatically tracks count changes
  const doubled = count * 2;
  console.log('Doubled:', doubled);
});
HookReturnsUse Case
useSignalSignal objectWhen you need to read AND write
useSignalValueRaw valueWhen you only need to read (optimized)
useSignalEffectvoidRun side effects when signals change
1️⃣

useSignal()

Create reactive state. Just like useState but better!

const [count, setCount] = useSignal(0);

// Read
{count}

// Write
setCount(5);
✅ Best for: Component local state
2️⃣

useSignalValue()

Read a signal created elsewhere. Read-only access.

// In store.ts
export const user = signal({...});

// In component
const userData = useSignalValue(user);

{userData.name}
✅ Best for: Reading global signals
3️⃣

useSignalEffect()

Run code when signals change. Auto-tracks dependencies!

useSignalEffect(() => {
  console.log(count);
  
  return () => {
    // cleanup
  };
});
✅ Best for: Side effects, logging, API calls

💻 Complete Real-World Example

import { useSignal, useSignalValue, useSignalEffect } from 'signalforge/react';
import { createSignal } from 'signalforge';

// Global signal (outside component)
const globalCounter = createSignal(0);

function ParentComponent() {
  // Local signal with useSignal
  const [name, setName] = useSignal('Guest');
  
  return (
    <div>
      <input 
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <ChildComponent />
    </div>
  );
}

function ChildComponent() {
  // Read global signal with useSignalValue
  const count = useSignalValue(globalCounter);
  
  // Effect runs when count changes
  useSignalEffect(() => {
    console.log(`Count changed to: ${count}`);
    
    // Cleanup (optional)
    return () => {
      console.log('Cleaning up...');
    };
  });
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => globalCounter.set(globalCounter.get() + 1)}>
        Increment Global Counter
      </button>
    </div>
  );
}

// ✨ Key Benefits:
// • useSignal for local state
// • useSignalValue for reading global state
// • useSignalEffect for side effects
// • All auto-track dependencies!

💡 Best Practices & Tips

1️⃣
Use useSignal for local state

Perfect for component-specific state like form inputs, toggles, counters.

2️⃣
Use useSignalValue for reading shared state

When you have a global signal and only need to read it (not write).

3️⃣
Use useSignalEffect for side effects

API calls, logging, timers, subscriptions. Always return cleanup functions!

4️⃣
No dependency arrays needed!

SignalForge automatically tracks what signals you use. Zero boilerplate!

🎯 Common Patterns

✅ Good: Local State
const [count, setCount] = useSignal(0);
<button onClick={() => setCount(count + 1)}>
✅ Good: Read Global
const user = useSignalValue(userSignal);
<h1>{user.name}</h1>
✅ Good: Side Effect
useSignalEffect(() => {
  fetchData(userId);
});
❌ Don't: Mix with useState
const [count] = useState(signal(0));
// Use useSignal instead!

🎓 Next Steps

Now you know all the hooks! Try building something: