Reputation: 8829
I have some duplicating code across my project that looks like this:
function MyComp({prop1, prop2, ...restProps}) {
// ...
const dataAttributes = Object.keys(restProps).reduce((acc, key) => {
if (key.startsWith('data-')) {
acc[key] = restProps[key];
}
return acc;
}, {});
return (
<div {...dataAttributes}>
{/* ... */}
</div>
);
}
I'm thinking about automation of this behaviour (pass props that start with data-
to the root element of the component if it's an html element).
Any thoughts?
Upvotes: 4
Views: 2993
Reputation: 10382
you could create a HOC function, where you can pass the remaining props to Component:
const withDataAttribute = (Component) => (props) => {
let passThroughProps = { ...props }
const dataAttributes = Object.keys(passThroughProps).reduce((acc, key) => {
if (key.startsWith('data-')) {
acc[key] = passThroughProps[key];
const { [key]: any, ...rest } = passThroughProps
passThroughProps = rest
}
return acc;
}, {});
return (
<div {...dataAttributes}>
<Component {...passThroughProps} />
</div>
);
}
Upvotes: 1
Reputation: 76
Maybe you just can create a custom component for the html tags you use, like RichDiv, RichSpan... It would be like:
const getDataAttributes = (inputProps = {}) => Object.keys(inputProps).reduce((acc, key) => {
if (key.startsWith('data-')) {
acc[key] = inputProps[key];
}
return acc;
}, {});
function RichDiv({children, className, ...restProps}) {
const dataAttributes = getDataAttributes(restProps);
return (
<div {...dataAttributes} className={className}>
{children}
</div>
);
}
function RichSpan({children, className, ...restProps}) {
const dataAttributes = getDataAttributes(restProps);
return (
<span {...dataAttributes} className={className}>
{children}
</span>
);
}
You can add any other whitelisted prop like I did with className to the custom components or even improve the props filter function you made to make it return both data- attributes and other HTML common attributes. Then, you just import and use your RichDiv instead of vanilla div and so.
Upvotes: 2
Reputation: 690
The first and not very complex step might be the extraction of reducing part to a separate utility function like this:
export function extractDataAttributes(props) {
return Object.keys(props).reduce((acc, key) => {
if (key.startsWith("data-")) {
acc[key] = props[key];
}
return acc;
}, {});
}
Then you can use it in the following shorter way:
function MyComp({prop1, prop2, ...restProps}) {
// ...
return (
<div {...extractDataAttributes(restProps)}>
{/* ... */}
</div>
);
}
By doing so, you'll also explicitly mark that the resulting markup has data-
attributes. On the other hand, there will be no unnecessary details.
If you find this solution insufficient at some point, it might be a good idea to use HOC looking inside its children and updating them. However, this approach might lower the explicitness of your code.
Upvotes: 1