Himmel Himmel - 5 months ago 34
Javascript Question

Can't unmount React component, returning false from parent component

I'm trying to perform authorization on a child component against certain permissions. I'm using

ref
callback to get access to the Node, wherein I can check permissions. Depending on these permissions, I would like to unmount the component.

Within the callback, I'm trying to use
ReactDOM.findDOMNode()
and then
ReactDOM.unmountComponentAtNode()
to remove it. The latter keeps returning false, although
findDomNode
appears to properly be selecting the DOM element.

class Auth extends React.Component {

...

checkPermissions(component) {
const domNode = ReactDOM.findDOMNode(component); // => <p>...</p>

if (domNode) {
let wasUnmounted = ReactDOM.unmountComponentAtNode(domNode);
console.log('was unmounted', wasUnmounted); // => false
}
}

render(){
return (
<div>
{this.state.authorized &&
<Component ref={(c) => this.checkPermissions(c)} {...this.props} />
}
</div>
)
}

...


How can I use
ReactDOM.unmountComponentAtNode()
to effectively remove my component?

Answer

I don't think you'll want to mount your node just to check permissions and then unmount it. You should check permissions before you render. Not only is it more secure, but it's also simpler.

If the user is authorized, you render the component. If the user is not authorized, you render something else.

So something kind of like this:

render() {
  if (!this.state.authorized) {
    return <PleaseLogIn />;
  }

  return (
    <div>
      <Component {...this.props} />
    </div>
  );
}

If you find yourself manipulating the DOM manually, take a step back and make sure there's not a more "Reacty" way to do it.

Update:

If you want a wrapper component that you can put around things that should or shouldn't render its children based on permissions maybe you do something like this:

// state.userPermissions = ['permission1', 'permission1', 'betaTolerant'];

const AuthWrapper = React.createClass({
  propTypes: {
    userPermissions: React.PropTypes.array.isRequired,
    requiredPermissions: React.PropTypes.array.isRequired,
    children: React.PropTypes.node.isRequired
  },

  isAllowed() {
    const { userPermissions, requiredPermissions } = this.props;
    return requiredPermissions.some((requiredPermission) => {
      return userPermissions.some((userPermission) => {
        // If this ever returns true, isAllowed will return true
        // Meaning: If any of the requiredPermissions match
        // any of the userPermissions
        return requiredPermission === userPermission;
      });
    });
  },

  render {
    if(!this.isAllowed()) return null;
    return this.props.children;
  };
});

const mapStateToProps = (state) => {
  // Only this auth component has access to state
  return {
    userPermissions: state.userPermissions
  };
};

export default connect(
  mapStateToProps,
  null
)(AuthWrapper);

Now you can use this wrapper like:

// Inside some component
render {
  return (
    <MyApp>
      <NormalFeature />
      <AuthWrapper requiredPermissions=['secretFeature', 'betaTolerant']>
        <SecretFeature />
      </AuthWrapper>
    </MyApp>
  );
}
Comments