I am using a HOC in react to restrict access to a component but I get the following Warning: validateDOMNesting(...): cannot appear as a descendant of . The entire code for the HOC (as requested) is as follows:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import * as selectors from '../../store/selectors';
class AccessRestrictedComponent extends Component {
static propTypes = {
auth: PropTypes.object.isRequired,
mode: PropTypes.string.isRequired,
useWithList: PropTypes.bool,
checkItem: PropTypes.bool,
restrictByRole: PropTypes.bool,
restrictByMode: PropTypes.bool,
allowedRoles: PropTypes.array,
allowedModes: PropTypes.array,
children: PropTypes.node.isRequired,
};
static defaultProps = {
useWithList: false,
checkItem: null,
restrictByRole: false,
restrictByMode: false,
allowedRoles: [],
allowedModes: [],
};
render() {
const { auth, mode, restrictByRole, restrictByMode, allowedRoles, allowedModes, useWithList, checkItem } = this.props;
const { role } = auth;
if (useWithList && !checkItem) {
return (React.cloneElement(this.props.children, { ...this.props }));
}
if (restrictByRole && restrictByMode) {
// console.log('restricting role and mode ');
if (allowedRoles.includes(role) && allowedModes.includes(mode)) {
// console.log(`role: ${role} and mode: ${mode} allowed.`);
return (React.cloneElement(this.props.children, { ...this.props }));
} return null;
}
if (restrictByRole) {
// console.log('restricting role ');
if (allowedRoles.includes(role)) {
// console.log(`role: ${role} allowed.`);
return (React.cloneElement(this.props.children, { ...this.props }));
} return null;
}
if (restrictByMode) {
// console.log('restricting mode ');
if (allowedModes.includes(mode)) {
// console.log(`${mode} allowed.`);
return (React.cloneElement(this.props.children, { ...this.props }));
} return null;
}
// console.log('component unrestricted');
return (React.cloneElement(this.props.children, { ...this.props }));
}
}
const mapStateToProps = state => ({
state,
auth: selectors.getAuth(state),
mode: selectors.getMode(state),
});
export default connect(mapStateToProps)(AccessRestrictedComponent);
the component I am wrapping is a styled component NavButton. I use the HOC like this:
<AccessRestrictedComponent restrictByRole allowedRoles={[userRoles.ADMIN]}>
<NavButton onClick={() => this.setState({ set: true })}>
<Icon className="material-icons md-48" color={color}>{'wallpaper'}</Icon>
</NavButton>
</AccessRestrictedComponent>
and the styled component is like this:
export const NavButton = styled.button`
display:flex;
align-content:flex-end;
align-self:flex-end;
background: none;
border: none;
&:hover,
&:focus{
${Icon}{
cursor: pointer;
color: ${props => props.theme.theme === themeTypes.LIGHT ? colors.SLATE_BLUE_50 : colors.lightBlue};
border-radius: 4px;
outline: none;
background: linear-gradient(${colors.shuttleGrey}, ${colors.shuttleGrey}) padding-box,
repeating-linear-gradient(-45deg,
${colors.submarine} 0, ${colors.submarine} 25%, transparent 0, transparent 50%
) 0 / 8px 8px;
}
}
`;
I do not understand this warning because I am not nesting a button but when I inspect the UI with dev tools there are two buttons nested. Does anyone know what could be causing this? Is it styled components? the HOC, something else maybe? I guess a simple solution is to change the styled component to something other than a button but I feel this should be valid code. Anyways, any suggestions greatly appreciated.
The error happens because when you clone the element, you pass children to cloned child as props. So your parent clones the button
and sends another button as props to your cloned button.
Fix is simple, don't send children
as merged props.
render() {
const { children, ...rest } = this.props;
// ...rest
return React.cloneElement(children, { ...rest });
//...rest
}
This way you wont send children
as props to children
again.
Note: You should replace all your React.cloneElement()
calls