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:
- Immediate access - Returns current value instantly
- No subscription - Doesn't register component for updates
- No re-renders - Component won't update when state changes
- Always current - Always reflects the latest state value
- 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.