Components
Components are the building blocks of Refract applications. They're pure and reusable functions that take a lens and return JSX, similar to React components but with built-in reactivity. They allow you to break UI into independent and organized pieces that can be easy referencing.
Creating components
Refract components are create using createComponent. This allows you to make use of the component anywhere in the app without adding export identifier at the end of the function.
Here is a typical example of form component:
- A traditional component
- Refract Component
- Result
//with export identifier
import React, { useState } from "react";
export const Form = () => {
const [name, setName] = useState("World");
return (
<div>
<h1>Hello, {name}!</h1>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
</div>
);
};
//without export identifier
import { createComponent } from "refract";
const Greeting = createComponent(({ lens }) => {
const name = lens.useRefraction("World");
return (
<div>
<h1>Hello, {name.value}!</h1>
<input
value={name.value}
onChange={(e) => name.set(e.target.value)}
placeholder="Enter your name"
/>
</div>
);
});
Hello, World!
Component Props
Refract components can receive props just like React components by using lens. Props are pieces of data passed from a parent to a child component, following a specific rule that allows the data to be modified. They're useful when a component needs to work with a defined set of data rules.
For example:
- Parent Component
- Child Component
- Result
const App = createComponent(({ lens }) => {
const user = lens.useRefraction({ name: "Alice", age: 25 });
return (
<div>
<UserProfile
user={user.value}
onUpdate={(updates) => user.set({ ...user.value, ...updates })}
/>
</div>
);
});
const UserProfile = createComponent(({ lens, user, onUpdate }) => {
const isEditing = lens.useRefraction(false);
return (
<div className="user-profile">
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
{isEditing.value ? (
<EditForm
user={user}
onSave={(updates) => {
onUpdate(updates);
isEditing.set(false);
}}
onCancel={() => isEditing.set(false)}
/>
) : (
<button onClick={() => isEditing.set(true)}>Edit Profile</button>
)}
</div>
);
});
From this example, Parent component owns the data and track of the user object. While the child display it and ask the parent to update it without breaking the data rule. So if you add another variable to the props, you get an error message.
Component lifecycle
You can manage a component lifestyle through lens in term of starts, updates, or stops e.g Timer watch:
- Component Lifecycle
- Result
const Timer = createComponent(({ lens }) => {
const seconds = lens.useRefraction(0);
const isRunning = lens.useRefraction(false);
// Effect runs when component mounts and when dependencies change
lens.useEffect(() => {
if (!isRunning.value) return;
const interval = setInterval(() => {
seconds.set((s) => s + 1);
}, 1000);
// Cleanup function runs when effect re-runs or component unmounts
return () => clearInterval(interval);
}, [isRunning.value]);
return (
<div>
<h2>Timer: {seconds.value}s</h2>
<button onClick={() => isRunning.set(!isRunning.value)}>
{isRunning.value ? "Stop" : "Start"}
</button>
<button onClick={() => seconds.set(0)}>Reset</button>
</div>
);
});
Timer: 0s
Related APIs
- createComponent - Creating components
- useRefraction - Managing component state
- useEffect - Component lifecycle and effects
- Refractions - Understanding reactive state