Lenses
In Refract, a lens is a tool for focusing on a specific part of a larger state, letting you read and update that slice without touching unrelated data.
If a refraction is like giving someone a telescope to view the whole sky, a lens is the dial you turn to zoom in on one particular constellation.
The Need For Lenses
Imagine your app state is a big object:
const appState = {
user: { name: "Alex", email: "alex@example.com" },
settings: { theme: "dark", language: "en" },
notifications: []
};
Without lenses, changing settings.theme would require:
-
Extracting the full state
-
Updating the value
-
Writing it back without accidentally touching other parts
Lenses solve this by allowing you to:
-
Observe only the data you care about
-
Update just that slice, leaving the rest untouched
-
Keep your components smaller and more focused
Creating a Lens
You can create a lens from any existing refraction:
import { createRefraction, createLens } from 'refract';
const appState = createRefraction({
user: { name: "Alex", email: "alex@example.com" },
settings: { theme: "dark", language: "en" }
});
// Focus only on theme
const themeLens = createLens(appState, state => state.settings.theme);
themeLens now behaves like its own refraction — but it’s bound to appState.settings.theme.
Reading and Updating Through a Lens
Using useRefraction with a lens is just like using a refraction:
import { useRefraction } from 'refract';
function ThemeSwitcher() {
const theme = useRefraction(themeLens);
return (
<div>
<p>Current theme: {theme}</p>
<button onClick={() => themeLens.set("light")}>Switch to Light</button>
<button onClick={() => themeLens.set("dark")}>Switch to Dark</button>
</div>
);
}
Updating via a lens automatically updates the parent refraction — and any other lenses linked to it.
Nested Lenses
You can create lenses from other lenses to drill deeper:
const userLens = createLens(appState, state => state.user);
const nameLens = createLens(userLens, user => user.name);
nameLens only cares about appState.user.name, keeping re-renders minimal.
Derived Lenses
A derived lens applies a transformation when reading/writing:
import { deriveLens } from 'refract';
const uppercaseThemeLens = deriveLens(themeLens, theme => theme.toUpperCase());
Here, uppercaseThemeLens always returns the theme in uppercase when read.
For very small, simple state objects (especially if they’re not shared), lenses might be overkill. Using a plain refraction can be simpler.