Skip to content

Using Property .value

Overview

The .value property provides direct access to the current state value without triggering React component re-renders. This is perfect for JavaScript logic, event handlers, and situations where you need the current value but don't want your component to automatically update when the state changes.

tsx
const currentValue = signify.value; // Get current value directly

Key Differences: .value vs .use()

Feature.value.use()
Triggers re-render❌ No✅ Yes
Reactive updates❌ No subscription✅ Auto-subscribes
Use in components⚠️ Static snapshot✅ Live updates
Use in event handlers✅ Perfect for this⚠️ Overkill
Use in useEffect✅ Great for side effects❌ Creates unnecessary deps
Performance✅ Zero overhead⚠️ Subscription overhead

How It Works

The .value property is a simple getter that returns the current state value immediately.

Key characteristics:

  1. Immediate access - Returns current value instantly
  2. No subscription - Doesn't register component for updates
  3. No re-renders - Component won't update when state changes
  4. Always current - Always reflects the latest state value
  5. Zero overhead - Simple property access

When to Use .value

✅ Perfect for Event Handlers

tsx
import { signify } from "react-signify";

const sCounter = signify(0);
const sMessage = signify("Hello");

function MyComponent() {
  const counter = sCounter.use(); // ✅ Subscribe for display
  
  const handleClick = () => {
    // ✅ Use .value in event handlers - no re-render needed
    const current = sCounter.value; 
    console.log("Current counter:", current);
    
    sCounter.set(current + 1);
  };
  
  const handleReset = () => {
    // ✅ Access current value for logic
    if (sCounter.value > 10) {
      sCounter.set(0);
      sMessage.set(`Reset from ${sCounter.value}`);
    }
  };

  return (
    <div>
      <p>Counter: {counter}</p> {/* ✅ Uses .use() for display */}
      <button onClick={handleClick}>Increment</button>
      <button onClick={handleReset}>Reset if > 10</button>
    </div>
  );
}

✅ Perfect for useEffect and Side Effects

tsx
const sUser = signify({ name: "John", age: 25 });
const sSettings = signify({ theme: "light", lang: "en" });

function UserProfile() {
  const user = sUser.use(); // ✅ Subscribe for display
  
  useEffect(() => {
    // ✅ Use .value to avoid unnecessary dependencies
    const currentSettings = sSettings.value;
    
    // Save to localStorage with current user and settings
    localStorage.setItem('userProfile', JSON.stringify({
      user: sUser.value,    // Current user
      settings: currentSettings  // Current settings
    }));
    
    // No need to add sSettings to dependency array!
  }, [user]); // Only depends on user changes
  
  return <div>Welcome, {user.name}!</div>;
}

✅ Perfect for Conditional Logic

tsx
const sAuthUser = signify(null);
const sCartItems = signify([]);
const sIsLoading = signify(false);

function ShoppingCart() {
  const cartItems = sCartItems.use(); // ✅ Subscribe for display
  
  const handleCheckout = async () => {
    // ✅ Use .value for conditional logic
    if (!sAuthUser.value) {
      alert("Please login first");
      return;
    }
    
    if (sCartItems.value.length === 0) {
      alert("Cart is empty");
      return;
    }
    
    if (sIsLoading.value) {
      return; // Prevent double submission
    }
    
    sIsLoading.set(true);
    
    try {
      await processCheckout(sCartItems.value, sAuthUser.value);
      sCartItems.set([]);
    } catch (error) {
      console.error(error);
    } finally {
      sIsLoading.set(false);
    }
  };

  return (
    <div>
      <h3>Cart ({cartItems.length} items)</h3>
      {cartItems.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
      <button onClick={handleCheckout}>Checkout</button>
    </div>
  );
}

✅ Perfect for External API Integration

tsx
const sApiData = signify(null);
const sApiError = signify(null);

function DataFetcher() {
  const data = sApiData.use(); // ✅ Subscribe for display
  const error = sApiError.use(); // ✅ Subscribe for display
  
  const fetchData = async () => {
    // ✅ Use .value to check current state without subscribing
    if (sApiData.value && !sApiError.value) {
      console.log("Data already loaded:", sApiData.value);
      return;
    }
    
    try {
      const response = await fetch("/api/data");
      const result = await response.json();
      
      sApiData.set(result);
      sApiError.set(null);
    } catch (err) {
      sApiError.set(err.message);
      
      // Use current data state in error handling
      console.log("Failed to fetch, current data:", sApiData.value);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  if (error) return <div>Error: {error}</div>;
  if (!data) return <div>Loading...</div>;
  
  return <div>Data: {JSON.stringify(data)}</div>;
}

When NOT to Use .value

❌ Don't Use for Component Display

tsx
const sCounter = signify(0);

function BadComponent() {
  // ❌ WRONG: Component won't update when counter changes
  const counter = sCounter.value;
  
  return <div>Count: {counter}</div>; // Will always show initial value!
}

function GoodComponent() {
  // ✅ CORRECT: Component updates when counter changes
  const counter = sCounter.use();
  
  return <div>Count: {counter}</div>; // Always shows current value
}

❌ Don't Use When You Need Reactivity

tsx
const sUser = signify({ name: "John", online: false });

function BadUserStatus() {
  // ❌ WRONG: Won't update when user.online changes
  const isOnline = sUser.value.online;
  
  return (
    <div style={{ color: isOnline ? 'green' : 'red' }}>
      {isOnline ? 'Online' : 'Offline'}
    </div>
  );
}

function GoodUserStatus() {
  // ✅ CORRECT: Updates when user.online changes
  const isOnline = sUser.use(user => user.online);
  
  return (
    <div style={{ color: isOnline ? 'green' : 'red' }}>
      {isOnline ? 'Online' : 'Offline'}
    </div>
  );
}

Advanced Use Cases

1. Complex Event Handling

tsx
const sFormData = signify({
  username: "",
  email: "",
  password: ""
});

const sValidationErrors = signify({});

function RegistrationForm() {
  const formData = sFormData.use(); // ✅ For display
  const errors = sValidationErrors.use(); // ✅ For display
  
  const validateField = (field: string, value: string) => {
    // ✅ Use .value to access current validation state
    const currentErrors = sValidationErrors.value;
    
    // Perform validation
    const newErrors = { ...currentErrors };
    
    if (field === 'email' && !value.includes('@')) {
      newErrors.email = 'Invalid email';
    } else {
      delete newErrors.email;
    }
    
    sValidationErrors.set(newErrors);
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    
    // ✅ Get current form data for submission
    const currentData = sFormData.value;
    const currentErrors = sValidationErrors.value;
    
    if (Object.keys(currentErrors).length > 0) {
      alert("Please fix validation errors");
      return;
    }
    
    // Submit with current data
    submitForm(currentData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.username}
        onChange={(e) => {
          sFormData.set(prev => {
            prev.value.username = e.target.value;
          });
          validateField('username', e.target.value);
        }}
      />
      {errors.username && <span>{errors.username}</span>}
      
      <button type="submit">Register</button>
    </form>
  );
}

2. State Synchronization

tsx
const sPrimaryCounter = signify(0);
const sSecondaryCounter = signify(0);

function CounterSync() {
  const primary = sPrimaryCounter.use(); // ✅ For display
  const secondary = sSecondaryCounter.use(); // ✅ For display
  
  const syncCounters = () => {
    // ✅ Use .value to get current states without subscribing
    const primaryVal = sPrimaryCounter.value;
    const secondaryVal = sSecondaryCounter.value;
    
    const total = primaryVal + secondaryVal;
    
    // Sync both to average
    const average = Math.floor(total / 2);
    sPrimaryCounter.set(average);
    sSecondaryCounter.set(average);
  };
  
  const resetToMax = () => {
    // ✅ Compare current values
    const max = Math.max(sPrimaryCounter.value, sSecondaryCounter.value);
    sPrimaryCounter.set(max);
    sSecondaryCounter.set(max);
  };

  return (
    <div>
      <div>Primary: {primary}</div>
      <div>Secondary: {secondary}</div>
      <button onClick={syncCounters}>Sync to Average</button>
      <button onClick={resetToMax}>Reset to Max</button>
    </div>
  );
}

3. Performance Monitoring

tsx
const sMetrics = signify({
  renderCount: 0,
  lastUpdate: Date.now(),
  updateFrequency: 0
});

function PerformanceMonitor() {
  const metrics = sMetrics.use(); // ✅ For display
  
  useEffect(() => {
    // ✅ Use .value to calculate metrics without extra subscriptions
    const currentMetrics = sMetrics.value;
    const now = Date.now();
    const timeDiff = now - currentMetrics.lastUpdate;
    
    sMetrics.set(prev => {
      prev.value.renderCount += 1;
      prev.value.lastUpdate = now;
      prev.value.updateFrequency = timeDiff > 0 ? 1000 / timeDiff : 0;
    });
  });
  
  const resetMetrics = () => {
    // ✅ Reset with awareness of current state
    console.log("Resetting metrics. Final count:", sMetrics.value.renderCount);
    
    sMetrics.set({
      renderCount: 0,
      lastUpdate: Date.now(),
      updateFrequency: 0
    });
  };

  return (
    <div>
      <div>Renders: {metrics.renderCount}</div>
      <div>Frequency: {metrics.updateFrequency.toFixed(2)} Hz</div>
      <button onClick={resetMetrics}>Reset</button>
    </div>
  );
}

Best Practices

1. Use .value for Logic, .use() for Display

tsx
function TodoApp() {
  const todos = sTodos.use(); // ✅ For rendering
  
  const addTodo = (text: string) => {
    // ✅ Use .value for logic
    const currentTodos = sTodos.value;
    const newId = currentTodos.length > 0 
      ? Math.max(...currentTodos.map(t => t.id)) + 1 
      : 1;
    
    sTodos.set(prev => {
      prev.value.push({ id: newId, text, completed: false });
    });
  };
  
  return (
    <div>
      {todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
      <AddTodoForm onAdd={addTodo} />
    </div>
  );
}

2. Combine with Other Signify Features

tsx
const sAppState = signify({
  user: null,
  theme: 'light',
  notifications: []
});

function App() {
  const user = sAppState.use(state => state.user); // ✅ Specific subscription
  
  // ✅ Use .value for complex state checks
  const handleThemeToggle = () => {
    const current = sAppState.value;
    
    sAppState.set(prev => {
      prev.value.theme = current.theme === 'light' ? 'dark' : 'light';
      
      // Add notification about theme change
      prev.value.notifications.push({
        id: Date.now(),
        message: `Switched to ${prev.value.theme} theme`,
        timestamp: new Date()
      });
    });
  };
  
  return (
    <div className={sAppState.value.theme}>
      {user ? <UserDashboard /> : <LoginForm />}
      <button onClick={handleThemeToggle}>Toggle Theme</button>
    </div>
  );
}

3. Debugging and Development

tsx
const sDebugMode = signify(false);
const sAppData = signify({ users: [], posts: [] });

function DebugPanel() {
  const debugMode = sDebugMode.use(); // ✅ For display
  
  const logCurrentState = () => {
    // ✅ Use .value for debugging without affecting render
    console.log("Current app state:", {
      debugMode: sDebugMode.value,
      appData: sAppData.value,
      timestamp: new Date().toISOString()
    });
  };
  
  const exportState = () => {
    // ✅ Export current state
    const state = {
      debug: sDebugMode.value,
      app: sAppData.value
    };
    
    const blob = new Blob([JSON.stringify(state, null, 2)], {
      type: 'application/json'
    });
    
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'app-state.json';
    a.click();
  };

  if (!debugMode) return null;

  return (
    <div style={{ position: 'fixed', bottom: 0, right: 0, background: 'white' }}>
      <h4>Debug Panel</h4>
      <button onClick={logCurrentState}>Log State</button>
      <button onClick={exportState}>Export State</button>
    </div>
  );
}

Summary

The .value property is your go-to solution when you need:

  • Current state value without component re-renders
  • Event handler logic that reads state
  • useEffect dependencies optimization
  • Conditional operations based on current state
  • External API integration and side effects
  • Performance monitoring and debugging

Remember: Use .value for logic, use .use() for display!

This approach gives you the best of both worlds - efficient, non-reactive state access when you need it, and automatic reactive updates when you want them.

Released under the MIT License.