0
# Props & Event Utilities
1
2
Intelligent prop merging, event chaining, and DOM attribute filtering for React components. These utilities handle the complex logic of combining props from multiple sources while preserving type safety.
3
4
## Capabilities
5
6
### mergeProps Function
7
8
Intelligently merges multiple props objects with special handling for event handlers, CSS classes, and IDs.
9
10
```typescript { .api }
11
/**
12
* Intelligently merges multiple props objects
13
* - Chains event handlers (functions starting with 'on[A-Z]')
14
* - Combines CSS classes using clsx
15
* - Merges IDs using mergeIds
16
* - Later props override earlier ones for other properties
17
* @param args - Multiple props objects to merge
18
* @returns Merged props object with proper TypeScript typing
19
*/
20
function mergeProps<T extends Props[]>(...args: T): UnionToIntersection<TupleTypes<T>>;
21
```
22
23
**Usage Examples:**
24
25
```typescript
26
import { mergeProps } from "@react-aria/utils";
27
28
function Button({ className, onClick, ...props }) {
29
const defaultProps = {
30
className: "btn btn-default",
31
onClick: (e) => console.log("Button clicked"),
32
type: "button" as const
33
};
34
35
const userProps = {
36
className,
37
onClick,
38
...props
39
};
40
41
// Event handlers are chained, classes combined, other props override
42
const finalProps = mergeProps(defaultProps, userProps);
43
44
return <button {...finalProps} />;
45
}
46
47
// Usage
48
<Button
49
className="btn-primary" // Results in: "btn btn-default btn-primary"
50
onClick={(e) => console.log("User click")} // Both handlers will run
51
disabled={true} // Overrides type: "button"
52
/>
53
```
54
55
### chain Function
56
57
Chains multiple callback functions to execute in sequence with the same arguments.
58
59
```typescript { .api }
60
/**
61
* Chains multiple callback functions to execute in sequence
62
* @param callbacks - Variable number of callback functions
63
* @returns Function that calls all callbacks with same arguments
64
*/
65
function chain(...callbacks: any[]): (...args: any[]) => void;
66
```
67
68
**Usage Examples:**
69
70
```typescript
71
import { chain } from "@react-aria/utils";
72
73
function MyComponent() {
74
const handleClick1 = (e) => console.log("First handler");
75
const handleClick2 = (e) => console.log("Second handler");
76
const handleClick3 = (e) => console.log("Third handler");
77
78
// Chain multiple handlers
79
const chainedHandler = chain(handleClick1, handleClick2, handleClick3);
80
81
return <button onClick={chainedHandler}>Click me</button>;
82
}
83
84
// All three handlers will execute in order when button is clicked
85
```
86
87
### filterDOMProps Function
88
89
Filters props to include only valid DOM attributes, with configurable options for different use cases.
90
91
```typescript { .api }
92
/**
93
* Filters props to include only valid DOM attributes
94
* @param props - Component props to filter
95
* @param opts - Options object controlling which props to include
96
* @returns Filtered props object safe for DOM elements
97
*/
98
function filterDOMProps(
99
props: DOMProps,
100
opts?: FilterDOMPropsOptions
101
): DOMAttributes & AriaLabelingProps & GlobalDOMAttributes;
102
103
interface FilterDOMPropsOptions {
104
/** Include ARIA labeling props (aria-label, aria-labelledby, etc.) */
105
labelable?: boolean;
106
/** Include link-specific DOM props (href, target, etc.) */
107
isLink?: boolean;
108
/** Include global DOM attributes (id, className, style, etc.) */
109
global?: boolean;
110
/** Include DOM event handlers (onClick, onFocus, etc.) */
111
events?: boolean;
112
/** Additional prop names to include */
113
propNames?: Set<string>;
114
}
115
```
116
117
**Usage Examples:**
118
119
```typescript
120
import { filterDOMProps } from "@react-aria/utils";
121
122
function MyInput({ label, onValueChange, ...props }) {
123
// Filter out non-DOM props but keep labeling props
124
const domProps = filterDOMProps(props, {
125
labelable: true,
126
events: true
127
});
128
129
return (
130
<div>
131
{label && <label>{label}</label>}
132
<input {...domProps} />
133
</div>
134
);
135
}
136
137
// Usage - onValueChange will be filtered out, but aria-label will be kept
138
<MyInput
139
aria-label="Name field"
140
onValueChange={(value) => console.log(value)} // Filtered out
141
className="input-field" // Kept if global: true
142
onClick={(e) => console.log("Clicked")} // Kept because events: true
143
customProp="value" // Filtered out
144
/>
145
```
146
147
### Advanced mergeProps Usage
148
149
Complex scenarios with multiple prop sources and type preservation:
150
151
```typescript
152
import { mergeProps, filterDOMProps } from "@react-aria/utils";
153
154
function AdvancedButton({ variant = "primary", size = "medium", ...props }) {
155
// Base props with defaults
156
const baseProps = {
157
className: `btn btn-${variant} btn-${size}`,
158
type: "button" as const
159
};
160
161
// Accessibility props
162
const a11yProps = {
163
role: "button",
164
tabIndex: 0
165
};
166
167
// User props (filtered for DOM safety)
168
const userProps = filterDOMProps(props, {
169
labelable: true,
170
events: true,
171
global: true
172
});
173
174
// Merge all props in order of precedence
175
const finalProps = mergeProps(baseProps, a11yProps, userProps);
176
177
return <button {...finalProps} />;
178
}
179
```
180
181
### Event Handler Chaining
182
183
Understanding how mergeProps chains event handlers:
184
185
```typescript
186
import { mergeProps } from "@react-aria/utils";
187
188
function EventExample() {
189
const props1 = {
190
onClick: (e) => {
191
console.log("First handler");
192
e.preventDefault(); // This will still execute
193
}
194
};
195
196
const props2 = {
197
onClick: (e) => {
198
console.log("Second handler");
199
// This runs after props1.onClick
200
}
201
};
202
203
const props3 = {
204
onClick: (e) => {
205
console.log("Third handler");
206
// This runs after props2.onClick
207
}
208
};
209
210
const mergedProps = mergeProps(props1, props2, props3);
211
212
return <button {...mergedProps}>Click me</button>;
213
// All three handlers execute in order: First, Second, Third
214
}
215
```
216
217
## Types
218
219
```typescript { .api }
220
interface DOMProps {
221
id?: string;
222
}
223
224
interface DOMAttributes extends React.DOMAttributes<HTMLElement> {
225
// Standard DOM event handlers and attributes
226
}
227
228
interface AriaLabelingProps {
229
"aria-label"?: string;
230
"aria-labelledby"?: string;
231
"aria-describedby"?: string;
232
"aria-details"?: string;
233
}
234
235
interface GlobalDOMAttributes {
236
className?: string;
237
style?: React.CSSProperties;
238
hidden?: boolean;
239
lang?: string;
240
dir?: "ltr" | "rtl";
241
// ... other global HTML attributes
242
}
243
244
type Props = Record<string, any>;
245
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
246
type TupleTypes<T> = { [P in keyof T]: T[P] };
247
```