Skip to main content

createComponent

createComponent is how you build the building blocks of your Refract application. It takes a function and turns it into a reactive component that automatically updates when your data changes.

Think of it as the foundation of your UI - every interactive element, every piece of content that needs to respond to user actions or data changes starts with createComponent.

Why createComponent?​

In traditional React, you write components that manage their own state and manually handle updates. With Refract, createComponent creates components that are inherently reactive - they automatically stay in sync with your data without any extra work.

The key difference is the lens parameter - this is your gateway to Refract's reactive features. Through the lens, you can create reactive state, handle side effects, and build reusable logic patterns.

createComponent

Since v1.0.0

Creates a new Refract component. Components are pure functions that receive props and a lens object for accessing reactive features.

Signature

const Component = createComponent(({ lens, ...props }) => JSX)

Parameters

componentFunction(props: { lens: Lens, [key: string]: any }) => JSX.Elementrequired

Function that receives props and lens, returns JSX

Returns

RefractComponent

A Refract component that can be used in JSX

Example

import { createComponent } from "refract";

const MyComponent = createComponent(({ lens, title, onClick }) => {
const count = lens.useRefraction(0);

return (
<div>
<h2>{title}</h2>
<p>Count: {count.value}</p>
<button onClick={() => count.set(count.value + 1)}>
Increment
</button>
<button onClick={onClick}>
External Action
</button>
</div>
);
});

// Usage
<MyComponent title="My Counter" onClick={() => alert('Clicked!')} />

Comparison with Traditional Approaches​

Let's see how createComponent compares to traditional React component patterns:

Traditional React Component
import React, { useState } from 'react';

const Counter = ({ title, onReset }) => {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('');

// Manual state synchronization

const increment = () => {
setCount(prev => prev + 1);
};

const reset = () => {
setCount(0);
onReset?.();
};

return (
<div>
<h2>{title}</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={reset}>Reset</button>
</div>e

);
};

export default Counter;

Issues with React components:

  • Manual state synchronization with useEffect
  • Multiple useState calls for related data
  • Must remember to export components
  • Verbose for simple reactive behavior

Basic Usage​

Simple Component

Creating a basic Refract component:

import { createComponent } from "refract";

const Greeting = createComponent(({ lens, name }) => {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Welcome to Refract</p>
</div>
);
});

// Usage
const App = createComponent(({ lens }) => {
return (
<div>
<Greeting name="World" />
<Greeting name="Refract" />
</div>
);
});

Component with State​

Stateful Component

Using the lens object to manage component state:

const Counter = createComponent(({ lens, initialValue = 0 }) => {
const count = lens.useRefraction(initialValue);
const message = lens.useRefraction("Click the button!");

const handleIncrement = () => {
count.set(count.value + 1);
message.set(`Clicked ${count.value + 1} times!`);
};

return (
<div className="counter">
<h2>{message.value}</h2>
<div className="count-display">
Count: {count.value}
</div>
<button onClick={handleIncrement}>
Increment
</button>
<button onClick={() => {
count.set(initialValue);
message.set("Reset to initial value!");
}}>
Reset
</button>
</div>
);
});

Component with Effects​

Component with Side Effects

Using lens methods for side effects and lifecycle management:

const UserProfile = createComponent(({ lens, userId }) => {
const user = lens.useRefraction(null);
const loading = lens.useRefraction(true);
const error = lens.useRefraction(null);

// Fetch user data when userId changes
lens.useEffect(() => {
loading.set(true);
error.set(null);

fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
})
.then(userData => {
user.set(userData);
loading.set(false);
})
.catch(err => {
error.set(err.message);
loading.set(false);
});
}, [userId]);

// Update document title when user data loads
lens.useEffect(() => {
if (user.value) {
document.title = `${user.value.name} - Profile`;
}

return () => {
document.title = "My App"; // Cleanup
};
}, [user.value?.name]);

if (loading.value) return <div>Loading user...</div>;
if (error.value) return <div>Error: {error.value}</div>;
if (!user.value) return <div>User not found</div>;

return (
<div className="user-profile">
<img src={user.value.avatar} alt={user.value.name} />
<h1>{user.value.name}</h1>
<p>{user.value.email}</p>
<p>Joined: {new Date(user.value.createdAt).toLocaleDateString()}</p>
</div>
);
});

Component Composition​

Composing Components

Building complex UIs by composing simple components:

const Button = createComponent(({ lens, children, variant = "primary", onClick }) => {
const isPressed = lens.useRefraction(false);

return (
<button
className={`btn btn--${variant} ${isPressed.value ? 'btn--pressed' : ''}`}
onMouseDown={() => isPressed.set(true)}
onMouseUp={() => isPressed.set(false)}
onMouseLeave={() => isPressed.set(false)}
onClick={onClick}
>
{children}
</button>
);
});

const Modal = createComponent(({ lens, children, title, onClose }) => {
const isVisible = lens.useRefraction(true);

// Handle escape key
lens.useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape') {
onClose();
}
};

document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, []);

if (!isVisible.value) return null;

return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h2>{title}</h2>
<Button variant="secondary" onClick={onClose}>
×
</Button>
</div>
<div className="modal-body">
{children}
</div>
</div>
</div>
);
});

// Usage
const App = createComponent(({ lens }) => {
const showModal = lens.useRefraction(false);

return (
<div>
<Button onClick={() => showModal.set(true)}>
Open Modal
</Button>

{showModal.value && (
<Modal
title="Example Modal"
onClose={() => showModal.set(false)}
>
<p>This is modal content!</p>
<Button onClick={() => showModal.set(false)}>
Close
</Button>
</Modal>
)}
</div>
);
});