Skip to main content

State Management Best Practices

Learn how to effectively manage reactive state in your Refract applications.

1. Immutable Updates​

Always create new objects and arrays when updating complex state:

Immutable Update Patterns
const MyComponent = createComponent(({ lens }) => {
const state = lens.useRefraction({
user: { name: "John", age: 25 },
items: [1, 2, 3],
settings: { theme: "light" }
});

// ✅ Correct - creates new objects/arrays
const updateUserName = (newName) => {
state.set({
...state.value,
user: {
...state.value.user,
name: newName
}
});
};

const addItem = (item) => {
state.set({
...state.value,
items: [...state.value.items, item]
});
};

return <div>Component content</div>;
});

2. Use Local State by Default​

Start with local state and only move to global when needed:

Component-Specific State
// ✅ Good - local state for component-specific data
const Modal = createComponent(({ lens, children, onClose }) => {
const isAnimating = lens.useRefraction(false); // Only this modal needs this

return (
<div className={`modal ${isAnimating.value ? 'animating' : ''}`}>
{children}
</div>
);
});

3. Initialize with Appropriate Types​

Use proper initial values that match your expected data structure:

Proper Initialization
const MyComponent = createComponent(({ lens }) => {
// ✅ Good - proper initial values
const count = lens.useRefraction(0); // number
const message = lens.useRefraction(""); // string
const items = lens.useRefraction([]); // array
const user = lens.useRefraction(null); // nullable object
const settings = lens.useRefraction({ // object with defaults
theme: "light",
notifications: true
});

return <div>Component content</div>;
});

4. State Organization Patterns​

Flat State Structure​

Keep state flat when possible for easier updates:

Flat State Structure
const UserForm = createComponent(({ lens }) => {
// ✅ Good - flat structure
const firstName = lens.useRefraction("");
const lastName = lens.useRefraction("");
const email = lens.useRefraction("");
const errors = lens.useRefraction({});

const handleSubmit = () => {
const userData = {
firstName: firstName.value,
lastName: lastName.value,
email: email.value
};
// Submit userData
};

return (
<form onSubmit={handleSubmit}>
<input
value={firstName.value}
onChange={(e) => firstName.set(e.target.value)}
/>
{/* More form fields */}
</form>
);
});

Group related state when it makes sense:

Grouped State
const ShoppingCart = createComponent(({ lens }) => {
const cart = lens.useRefraction({
items: [],
total: 0,
discount: 0,
shipping: 0
});

const addItem = (item) => {
const newItems = [...cart.value.items, item];
const newTotal = calculateTotal(newItems);

cart.set({
...cart.value,
items: newItems,
total: newTotal
});
};

return <div>Cart content</div>;
});

5. Performance Considerations​

Minimize Re-renders​

Split state to minimize unnecessary re-renders:

Optimized State Splitting
const Dashboard = createComponent(({ lens }) => {
// Split frequently changing data
const notifications = lens.useRefraction([]);
const userProfile = lens.useRefraction(null);
const settings = lens.useRefraction({ theme: 'light' });

// This way, changing notifications doesn't re-render
// components that only depend on userProfile

return (
<div>
<UserProfile user={userProfile.value} />
<NotificationList notifications={notifications.value} />
<SettingsPanel settings={settings.value} />
</div>
);
});

Use Computed Values​

Derive expensive calculations with computed values:

Computed Values
const ProductList = createComponent(({ lens, products }) => {
const searchQuery = lens.useRefraction("");
const sortBy = lens.useRefraction("name");

// ✅ Good - computed value for expensive operations
const filteredProducts = lens.useComputed(() => {
return products
.filter(p => p.name.toLowerCase().includes(searchQuery.value.toLowerCase()))
.sort((a, b) => a[sortBy.value].localeCompare(b[sortBy.value]));
}, [products, searchQuery.value, sortBy.value]);

return (
<div>
<input
value={searchQuery.value}
onChange={(e) => searchQuery.set(e.target.value)}
/>
{filteredProducts.value.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
});