Hone logo
Hone
Problems

React usePermission Hook Implementation

Create a custom React hook called usePermission that helps manage and track user permissions for specific features or actions within an application. This hook will simplify the process of conditionally rendering UI elements or executing logic based on whether a user has the necessary permissions.

Problem Description

Your task is to implement a reusable React hook, usePermission, in TypeScript. This hook should accept a permission string (e.g., 'canEdit', 'isAdmin') and return a boolean value indicating whether the current user has that specific permission.

Key Requirements:

  1. Hook Signature: The hook should have a signature similar to usePermission(permissionKey: string): boolean.
  2. Permission Management: The hook needs a way to access or retrieve the current user's permissions. For this challenge, you can assume a global or context-based mechanism for fetching permissions, but the hook itself should abstract this detail.
  3. Return Value: The hook must return true if the user has the specified permission, and false otherwise.
  4. Reactivity: The hook should be reactive. If the user's permissions change (e.g., after a login or role update), the hook should automatically re-evaluate and return the updated boolean value.

Expected Behavior:

  • When usePermission('canViewDashboard') is called, and the user has the 'canViewDashboard' permission, it should return true.
  • When usePermission('canEditProfile') is called, and the user does not have the 'canEditProfile' permission, it should return false.

Edge Cases to Consider:

  • Permission Not Found: What happens if the permissionKey provided is not recognized or not stored in the permission system? The hook should gracefully handle this, likely by returning false.
  • Initial Loading State: If permissions are fetched asynchronously, how should the hook behave while permissions are being loaded? It should probably return false until permissions are confirmed.

Examples

Example 1: Basic Usage

Imagine a component that should only show an "Edit" button if the user has canEdit permission.

// Assume usePermission is implemented and available
function EditableProfile({ user }) {
  const canEdit = usePermission('canEditProfile'); // This hook checks user's permissions

  return (
    <div>
      <h1>User Profile</h1>
      {canEdit && <button>Edit Profile</button>}
      {/* ... other profile details */}
    </div>
  );
}

Input:

  • permissionKey: 'canEditProfile'
  • Current User Permissions: ['canViewProfile', 'canEditProfile', 'canDeleteAccount']

Output:

  • The usePermission('canEditProfile') hook returns true.
  • The "Edit Profile" button is rendered.

Explanation: The hook checks if 'canEditProfile' exists in the user's granted permissions and returns true, allowing the button to be displayed.

Example 2: Missing Permission

A component that should only display an admin panel if the user is an administrator.

// Assume usePermission is implemented and available
function AdminDashboard() {
  const isAdmin = usePermission('isAdmin');

  if (!isAdmin) {
    return <div>You do not have access to the admin dashboard.</div>;
  }

  return (
    <div>
      <h2>Admin Dashboard</h2>
      {/* ... admin features */}
    </div>
  );
}

Input:

  • permissionKey: 'isAdmin'
  • Current User Permissions: ['canViewDashboard', 'canEditProfile']

Output:

  • The usePermission('isAdmin') hook returns false.
  • The component renders: <div>You do not have access to the admin dashboard.</div>

Explanation: The hook checks for 'isAdmin' in the user's permissions. Since it's not present, it returns false, and the component renders the access denied message.

Example 3: Handling Asynchronous Permissions (Conceptual)

If permissions are fetched from an API and are initially undefined or null.

// Assume usePermission is implemented and handles loading states
function FeatureGate({ children, requiredPermission }) {
  const hasPermission = usePermission(requiredPermission);

  // While permissions are loading, or if the permission is explicitly denied
  if (hasPermission === undefined || hasPermission === false) {
    return <div>Loading or Access Denied...</div>;
  }

  return <>{children}</>;
}

// Usage:
// <FeatureGate requiredPermission="canUseAdvancedFeature">
//   <AdvancedFeatureComponent />
// </FeatureGate>

Input:

  • permissionKey: 'canUseAdvancedFeature'
  • Current User Permissions: Initially undefined (loading), then ['canUseAdvancedFeature']

Output (During Loading):

  • usePermission('canUseAdvancedFeature') returns undefined (or a specific loading state indicator).
  • The FeatureGate component renders: <div>Loading or Access Denied...</div>

Output (After Loading):

  • usePermission('canUseAdvancedFeature') returns true.
  • The FeatureGate component renders its children (<AdvancedFeatureComponent />).

Explanation: The hook is designed to handle an initial state where permissions might not be immediately available, preventing rendering of protected content until the permission status is confirmed.

Constraints

  • The usePermission hook must be implemented in TypeScript.
  • The hook should not directly manage the fetching of permissions. It should rely on an external source (e.g., a context, a global store, or a mocked service) to provide the permission data. You will need to define an interface or type for how permissions are represented.
  • The solution should be performant for typical application scenarios (hundreds of components using the hook). Avoid expensive re-computations on every render if permissions haven't changed.
  • The permissionKey will always be a string.

Notes

  • Consider using React Context to provide the permission data to the usePermission hook. This is a common and idiomatic way to manage global or semi-global state in React.
  • Think about how to handle the initial state of permissions, especially if they are fetched asynchronously. A common pattern is to return undefined or a specific loading state while fetching.
  • You'll likely need to create a mock or a placeholder for the actual permission data source for testing and demonstration purposes.
  • The focus is on the usePermission hook's logic and its integration with a hypothetical permission system.
Loading editor...
typescript