Skip to main content

Optical Composition

Optical composition in Refract refers to the practice of building reusable logic and state patterns using "optics"—custom hooks that encapsulate stateful or side-effectful behavior. This approach allows you to compose complex features from simple, focused units of logic.

What is an Optic?

An optic is a function (custom hook) that uses the lens API to encapsulate reusable logic, state, or effects. Optics can be shared across components, making your codebase more modular and maintainable.

Creating an Optic

Here's a simple example of an optic for a counter:

function useCounter(lens, initial = 0) {
const count = lens.useRefraction(initial);
const increment = () => count.set(count.value + 1);
const decrement = () => count.set(count.value - 1);
return { count, increment, decrement };
}

Using Optics in Components

Use lens.useOptic to use your custom optic inside a component:

const Counter = createComponent(({ lens }) => {
const { count, increment, decrement } = lens.useOptic(useCounter, 0);
return (
<div>
<button onClick={decrement}>-</button>
<span>{count.value}</span>
<button onClick={increment}>+</button>
</div>
);
});

Composing Multiple Optics

You can combine multiple optics to build more complex features:

function useForm(lens, initial = {}) {
const values = lens.useRefraction(initial);
const setField = (name, value) => {
values.set({ ...values.value, [name]: value });
};
return { values, setField };
}

const SignupForm = createComponent(({ lens }) => {
const { values, setField } = lens.useOptic(useForm, {
email: "",
password: "",
});
const { count, increment } = lens.useOptic(useCounter, 0);

return (
<form>
<input
value={values.value.email}
onChange={(e) => setField("email", e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={values.value.password}
onChange={(e) => setField("password", e.target.value)}
placeholder="Password"
/>
<button type="button" onClick={increment}>
Submit ({count.value})
</button>
</form>
);
});

Sharing and Reusing Optics

Optics can be defined in separate files and imported wherever needed, promoting code reuse and separation of concerns.

Summary

Optical composition enables you to build scalable, maintainable applications by composing logic and state using optics. This pattern encourages modularity and clarity in your codebase.