skip to Main Content

I want to check user access from wrapper component before clicking on a button(or before a button onClick function fires).

Here is the button component

<UltimateButton
  btntext="+ New document"
  classes="btn-primary"
  handleClick={create_new_doc}
/>

Here handleClick fires onClick function (create_new_doc). And suppose I have a wrapper component <AccessControl> that wraps this button component. I want to check access when clicking AccessControl before create_new_doc is fired.

I wrote like this

import React, { Children, cloneElement } from "react";

const AccessControl = ({children}) => {
    const checkUserAccess = () => {
        console.log('Access denied')
    }

    return {...children}
}
export default AccessControl;

Now if access is denied in AccessControl then create_new_doc won’t fire anymore. How can I accomplish it. I also tried clone element.

This will be used like this

<AccessControl>
  <UltimateButton
    btntext="+ New document"
    classes="btn-primary"
    handleClick={create_new_doc}
  />
</AccessControl>

2

Answers


  1. You can do this in many ways. If you want to keep a similar flow ( using access wrapper) then you should have the wrapper itself handle the on click to conditionally render the button.

    Here is one simple way to do this (as aforementioned there are MANY ways to do this, let me know if you’d like a different version):

    import React from "react";
    
    const AccessControl = ({ hasAccess, handleClick, children }) => {
        const handleButtonClick = () => {
            if (hasAccess) {
                handleClick();
            } else {
                console.log('Access denied');
            }
        };
    
        return (
            <div>
                {hasAccess ? (
                    children
                ) : (
                    <button disabled onClick={handleButtonClick}>
                        Access Denied
                    </button>
                )}
            </div>
        );
    };
    
    export default AccessControl;
    

    Usage:

    <AccessControl hasAccess={checkUserAccess()}>
       <UltimateButton
         btntext="+ New document"
         classes="btn-primary"
         handleClick={create_new_doc}
       />
    </AccessControl>
    
    Login or Signup to reply.
  2. Seems like you are on the right track with the idea of cloning the child component.

    Clone the child component and re-inject a "decorated" handleClick prop that first makes a call to checkUserAccess and if this function returns true or confirms the user has access then it calls the originally passed handleClick callback.

    Example:

    const AccessControl = ({ children }) => {
      const checkUserAccess = () => {
        // Compute hasAccess
        console.log(hasAccess ? "Access allowed" : "Access denied");
        return hasAccess;
      };
    
      return Children.map(children, (child) =>
        cloneElement(child, {
          handleClick: (e) => checkUserAccess() && child.props.handleClick(e)
        })
      );
    };
    

    Demo

    const AccessControl = ({ children, hasAccess }) => {
      const checkUserAccess = () => {
        console.log(hasAccess ? "Access allowed" : "Access denied");
        return hasAccess;
      };
    
      return React.Children.map(children, (child) =>
        React.cloneElement(child, {
          handleClick: (e) => checkUserAccess() && child.props.handleClick(e)
        })
      );
    };
    
    const UltimateButton = ({ btntext, handleClick }) => (
      <button type="button" onClick={handleClick}>
        {btntext}
      </button>
    );
    
    const App = () => {
      const create_new_doc = () => console.log("create_new_doc");
      
       return (
        <div className="App">
           <UltimateButton
            btntext="No Access Control + New document"
            classes="btn-primary"
            handleClick={create_new_doc}
          />
    
          <AccessControl hasAccess>
            <UltimateButton
              btntext="Has access + New document"
              classes="btn-primary"
              handleClick={create_new_doc}
            />
          </AccessControl>
    
          <AccessControl>
            <UltimateButton
              btntext="No access + New document"
              classes="btn-primary"
              handleClick={create_new_doc}
            />
          </AccessControl>
        </div>
      );
    };
    
    const rootElement = document.getElementById("root");
    const root = ReactDOM.createRoot(rootElement);
    
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
    <div id="root" />
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search