I need a way to give the users of my React Web application the ability to deep-link to individual components (marked with a deeplinkKey
prop) on any page. A user would go to a URL which includes a component key. The app would then make visible the component(s) where the deeplinkKey
prop matches the given key only, hiding all other elements with deeplinkKey
props. (Elements without a deeplinkKey
prop would always show.)
I currently have a wrapper component that shows/hides elements within its child tree based on a whitelist (included below). However, the wrapper element can't see unrendered children.
How can I enable the user to link to hidden elements like 'resourceA' in this example and have <DeeplinkWrapper>
render them:
let condition = true;
<DeeplinkWrapper deeplinkWhitelist=['resourceA']>
<Root>
{condition ?
<ParentElement1>
<Element deeplinkKey='someResource' />
</ParentElement1>
:
<ParentElement2>
<Element deeplinkKey='resourceA' />
</ParentElement2>
}
</Root>
</DeeplinkWrapper>
/*
Desired "deep-linked" render:
<Root>
<ParentElement2>
<Element deeplinkKey='resourceA' />
</ParentElement2>
</Root>
*/
Here is my wrapper. It operates based on these rules:
// Any child element of <DeeplinkWrapper> will show if:
// The whitelist array is empty
// The child is pure text or null
// The child does not have the 'deeplinkKey' prop
// The child's 'deeplinkKey' prop exists and its value is in the whitelist array
// Any child element of <DeeplinkWrapper> will be hidden if:
// It's a child of a hidden parent
// The child's 'deeplinkKey' prop exists, but is not in the whitelist
// The child's 'deeplinkKey' prop exists, but is undefined, e.g. <span deeplinkKey>Text</span>
export default function DeeplinkWrapper({ children, whitelist = [] }) {
const showWhitelistedComponents = (children, fn) => {
return React.Children.map(children, child => {
const isReactElement = React.isValidElement(child);
const doShow = !isReactElement || !child.props.deeplinkKey || whitelist.indexOf(child.props.deeplinkKey) !== -1;
if (!isReactElement) {
return doShow ? child : null;
}
if (child.props.children) {
child = React.cloneElement(child, {
children: showWhitelistedComponents(child.props.children, fn)
});
}
return doShow ? fn(child) : null;
});
}
return (
<React.Fragment>
{whitelist.length === 0 ? React.Children.map(children, child => child) : showWhitelistedComponents(children, child => child)}
</React.Fragment>
)
}