0
# Pure Components
1
2
Lightweight component pattern for simple, stateless components without full lifecycle management. Pure components are optimized for performance and simplicity, ideal for presentational components or when you need fine-grained control over rendering.
3
4
## Capabilities
5
6
### Pure Component Factory
7
8
Creates a pure component factory function that produces lightweight component instances.
9
10
```typescript { .api }
11
/**
12
* Lift a riot component interface into a pure riot object
13
* @param func - RiotPureComponent factory function
14
* @returns The original function marked as pure for internal handling
15
*/
16
function pure<
17
InitialProps extends DefaultProps = DefaultProps,
18
Context = any,
19
FactoryFunction = PureComponentFactoryFunction<InitialProps, Context>
20
>(func: FactoryFunction): FactoryFunction;
21
```
22
23
**Usage Example:**
24
25
```javascript
26
import { pure } from "riot";
27
28
// Create a pure component factory
29
const createSimpleCard = pure(({ slots, attributes, props }) => {
30
return {
31
mount(element, context) {
32
element.innerHTML = `
33
<div class="card">
34
<h3>${props.title}</h3>
35
<p>${props.content}</p>
36
</div>
37
`;
38
},
39
40
update(context) {
41
// Update logic if needed
42
if (context && context.newContent) {
43
const p = element.querySelector('p');
44
p.textContent = context.newContent;
45
}
46
},
47
48
unmount(keepRootElement) {
49
if (!keepRootElement) {
50
element.innerHTML = '';
51
}
52
}
53
};
54
});
55
56
// Use the pure component
57
const element = document.getElementById("card-container");
58
const cardComponent = createSimpleCard({
59
props: { title: "Pure Card", content: "This is a pure component" }
60
});
61
62
cardComponent.mount(element);
63
```
64
65
## Pure Component Interface
66
67
Pure components implement a simplified interface without the full RiotComponent lifecycle:
68
69
```typescript { .api }
70
interface RiotPureComponent<Context = object> {
71
/** Mount component to DOM element */
72
mount(element: HTMLElement, context?: Context): void;
73
74
/** Update component with new context */
75
update(context?: Context): void;
76
77
/** Unmount component from DOM */
78
unmount(keepRootElement: boolean): void;
79
}
80
```
81
82
### Pure Component Factory Function
83
84
The factory function receives component metadata and returns a pure component instance:
85
86
```typescript { .api }
87
interface PureComponentFactoryFunction<
88
InitialProps extends DefaultProps = DefaultProps,
89
Context = any
90
> {
91
({
92
slots,
93
attributes,
94
props,
95
}: {
96
slots?: TagSlotData<Context>[];
97
attributes?: AttributeExpressionData<Context>[];
98
props?: InitialProps;
99
}): RiotPureComponent<Context>;
100
}
101
```
102
103
## Pure Component Patterns
104
105
### Simple Presentational Component
106
107
```javascript
108
import { pure } from "riot";
109
110
const createButton = pure(({ props, attributes }) => {
111
let element;
112
113
return {
114
mount(el) {
115
element = el;
116
element.innerHTML = `
117
<button class="btn ${props.variant || 'primary'}">
118
${props.label || 'Click me'}
119
</button>
120
`;
121
122
// Add event listeners
123
const button = element.querySelector('button');
124
button.addEventListener('click', props.onClick || (() => {}));
125
},
126
127
update(context) {
128
if (context && context.label) {
129
const button = element.querySelector('button');
130
button.textContent = context.label;
131
}
132
},
133
134
unmount(keepRootElement) {
135
if (!keepRootElement && element) {
136
element.innerHTML = '';
137
}
138
}
139
};
140
});
141
142
// Usage
143
const buttonComponent = createButton({
144
props: {
145
label: "Save",
146
variant: "success",
147
onClick: () => console.log("Saved!")
148
}
149
});
150
```
151
152
### Data Display Component
153
154
```javascript
155
const createDataTable = pure(({ props }) => {
156
let element;
157
158
return {
159
mount(el, context) {
160
element = el;
161
this.render(props.data || []);
162
},
163
164
update(context) {
165
if (context && context.data) {
166
this.render(context.data);
167
}
168
},
169
170
render(data) {
171
const tableHTML = `
172
<table class="data-table">
173
<thead>
174
<tr>${props.columns.map(col => `<th>${col.title}</th>`).join('')}</tr>
175
</thead>
176
<tbody>
177
${data.map(row => `
178
<tr>${props.columns.map(col => `<td>${row[col.key]}</td>`).join('')}</tr>
179
`).join('')}
180
</tbody>
181
</table>
182
`;
183
element.innerHTML = tableHTML;
184
},
185
186
unmount(keepRootElement) {
187
if (!keepRootElement && element) {
188
element.innerHTML = '';
189
}
190
}
191
};
192
});
193
194
// Usage
195
const tableComponent = createDataTable({
196
props: {
197
columns: [
198
{ key: 'name', title: 'Name' },
199
{ key: 'email', title: 'Email' }
200
],
201
data: [
202
{ name: 'Alice', email: 'alice@example.com' },
203
{ name: 'Bob', email: 'bob@example.com' }
204
]
205
}
206
});
207
```
208
209
### Stateful Pure Component
210
211
Even though called "pure", these components can maintain internal state:
212
213
```javascript
214
const createCounter = pure(({ props }) => {
215
let element;
216
let count = props.initial || 0;
217
218
return {
219
mount(el) {
220
element = el;
221
this.render();
222
223
// Add event listeners
224
element.addEventListener('click', (e) => {
225
if (e.target.matches('.increment')) {
226
count++;
227
this.render();
228
} else if (e.target.matches('.decrement')) {
229
count--;
230
this.render();
231
}
232
});
233
},
234
235
update(context) {
236
if (context && typeof context.count === 'number') {
237
count = context.count;
238
this.render();
239
}
240
},
241
242
render() {
243
element.innerHTML = `
244
<div class="counter">
245
<button class="decrement">-</button>
246
<span class="count">${count}</span>
247
<button class="increment">+</button>
248
</div>
249
`;
250
},
251
252
unmount(keepRootElement) {
253
if (!keepRootElement && element) {
254
element.innerHTML = '';
255
}
256
}
257
};
258
});
259
```
260
261
## Pure vs Regular Components
262
263
### When to Use Pure Components
264
265
- **Simple presentational components** without complex lifecycle needs
266
- **Performance-critical scenarios** where you need minimal overhead
267
- **Custom rendering logic** that doesn't fit the standard template system
268
- **Third-party integration** where you need direct DOM control
269
- **Micro-components** with very specific, limited functionality
270
271
### When to Use Regular Components
272
273
- **Complex state management** with lifecycle hooks
274
- **Template-based rendering** with expression bindings
275
- **Parent-child communication** through props and events
276
- **Plugin integration** for cross-cutting concerns
277
- **Standard component patterns** following Riot conventions
278
279
## Types
280
281
```typescript { .api }
282
interface RiotPureComponent<Context = object> {
283
mount(element: HTMLElement, context?: Context): void;
284
update(context?: Context): void;
285
unmount(keepRootElement: boolean): void;
286
}
287
288
interface PureComponentFactoryFunction<
289
InitialProps extends DefaultProps = DefaultProps,
290
Context = any
291
> {
292
({
293
slots,
294
attributes,
295
props,
296
}: {
297
slots?: TagSlotData<Context>[];
298
attributes?: AttributeExpressionData<Context>[];
299
props?: InitialProps;
300
}): RiotPureComponent<Context>;
301
}
302
303
type DefaultProps = Record<PropertyKey, any>;
304
305
type TagSlotData<Context = any> = {
306
id: string;
307
html: string;
308
bindings: BindingData<Context>[];
309
};
310
311
type AttributeExpressionData<Context = any> = {
312
name: string;
313
evaluate: (context?: Context) => any;
314
};
315
```