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:
- Hook Signature: The hook should have a signature similar to
usePermission(permissionKey: string): boolean. - 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.
- Return Value: The hook must return
trueif the user has the specified permission, andfalseotherwise. - 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 returntrue. - When
usePermission('canEditProfile')is called, and the user does not have the'canEditProfile'permission, it should returnfalse.
Edge Cases to Consider:
- Permission Not Found: What happens if the
permissionKeyprovided is not recognized or not stored in the permission system? The hook should gracefully handle this, likely by returningfalse. - Initial Loading State: If permissions are fetched asynchronously, how should the hook behave while permissions are being loaded? It should probably return
falseuntil 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 returnstrue. - 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 returnsfalse. - 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')returnsundefined(or a specific loading state indicator).- The
FeatureGatecomponent renders:<div>Loading or Access Denied...</div>
Output (After Loading):
usePermission('canUseAdvancedFeature')returnstrue.- The
FeatureGatecomponent renders itschildren(<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
usePermissionhook 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
permissionKeywill always be a string.
Notes
- Consider using React Context to provide the permission data to the
usePermissionhook. 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
undefinedor 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
usePermissionhook's logic and its integration with a hypothetical permission system.