Skip to main content

Prop Types

Common PropTypes definitions for consistent type checking across React components in the WorkPayCore frontend application.

ModalType

Standard PropType definition for modal components.

Properties:

  • isOpen (bool, required): Whether the modal is open
  • onClose (func, required): Function to close the modal

Example:

import PropTypes from 'prop-types';
import { ModalType } from '@/utils/propTypes';

const MyModal = ({ modal, children }) => {
return (
<Modal isOpen={modal.isOpen} onClose={modal.onClose}>
{children}
</Modal>
);
};

MyModal.propTypes = {
modal: ModalType.isRequired,
children: PropTypes.node,
};

Use Cases:

  • Consistent modal prop validation
  • Standardized modal interface
  • Documentation through PropTypes

User PropTypes

UserType

Standard PropType definition for user objects.

Properties:

  • id (string): User identifier
  • email (string): User email address
  • username (string): Username
  • profilePhoto (string): URL to profile photo

Example:

import { UserType } from '@/utils/propTypes';

const UserProfile = ({ user, onEdit }) => {
return (
<div className='user-profile'>
<img src={user.profilePhoto} alt={user.username} />
<h2>{user.username}</h2>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
);
};

UserProfile.propTypes = {
user: UserType.isRequired,
onEdit: PropTypes.func.isRequired,
};

Use Cases:

  • User component prop validation
  • Consistent user object structure
  • Type safety in user-related components

Usage Examples

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { ModalType } from '@/utils/propTypes';

const useModal = () => {
const [isOpen, setIsOpen] = useState(false);

const modal = {
isOpen,
onClose: () => setIsOpen(false),
};

const openModal = () => setIsOpen(true);

return { modal, openModal };
};

const ConfirmDialog = ({ modal, message, onConfirm }) => {
const handleConfirm = () => {
onConfirm();
modal.onClose();
};

return (
<Modal isOpen={modal.isOpen} onClose={modal.onClose}>
<div className='confirm-dialog'>
<p>{message}</p>
<button onClick={handleConfirm}>Confirm</button>
<button onClick={modal.onClose}>Cancel</button>
</div>
</Modal>
);
};

ConfirmDialog.propTypes = {
modal: ModalType.isRequired,
message: PropTypes.string.isRequired,
onConfirm: PropTypes.func.isRequired,
};
import { UserType } from '@/utils/propTypes';

const UserCard = ({ user, onClick }) => (
<div className='user-card' onClick={() => onClick(user)}>
<img src={user.profilePhoto} alt={user.username} />
<div>
<h3>{user.username}</h3>
<p>{user.email}</p>
</div>
</div>
);

UserCard.propTypes = {
user: UserType.isRequired,
onClick: PropTypes.func,
};

const UserList = ({ users, onUserSelect }) => (
<div className='user-list'>
{users.map(user => (
<UserCard key={user.id} user={user} onClick={onUserSelect} />
))}
</div>
);

UserList.propTypes = {
users: PropTypes.arrayOf(UserType).isRequired,
onUserSelect: PropTypes.func,
};

Extended PropTypes

// Extending base types for specific use cases
const ExtendedUserType = PropTypes.shape({
...UserType,
role: PropTypes.oneOf(['admin', 'user', 'manager']),
createdAt: PropTypes.string,
lastLogin: PropTypes.string,
});

const AdminUserCard = ({ user, onRoleChange }) => {
return (
<div className='admin-user-card'>
<UserCard user={user} />
<select
value={user.role}
onChange={e => onRoleChange(user.id, e.target.value)}
>
<option value='user'>User</option>
<option value='manager'>Manager</option>
<option value='admin'>Admin</option>
</select>
</div>
);
};

AdminUserCard.propTypes = {
user: ExtendedUserType.isRequired,
onRoleChange: PropTypes.func.isRequired,
};

Complex Modal Types

// More complex modal with additional options
const AdvancedModalType = PropTypes.shape({
...ModalType,
size: PropTypes.oneOf(['small', 'medium', 'large']),
closable: PropTypes.bool,
backdrop: PropTypes.oneOf(['static', 'clickable', 'none']),
});

const AdvancedModal = ({ modal, children }) => (
<Modal
isOpen={modal.isOpen}
onClose={modal.onClose}
size={modal.size || 'medium'}
backdrop={modal.backdrop || 'clickable'}
closable={modal.closable !== false}
>
{children}
</Modal>
);

AdvancedModal.propTypes = {
modal: AdvancedModalType.isRequired,
children: PropTypes.node,
};

Custom PropType Validators

You can extend these base types with custom validators:

const EmailUserType = PropTypes.shape({
...UserType,
email: (props, propName, componentName) => {
const email = props[propName];
if (email && !/\S+@\S+\.\S+/.test(email)) {
return new Error(
`Invalid email '${email}' supplied to '${componentName}'. Expected a valid email address.`,
);
}
},
});

Benefits

Type Safety

  • Runtime validation of props
  • Clear component interfaces
  • Documentation through types

Development Experience

  • PropTypes warnings in development
  • Self-documenting components
  • Consistent prop structures

Maintenance

  • Easier refactoring
  • Clear component contracts
  • Reduced bugs

Best Practices

  1. Always use PropTypes for component props validation
  2. Mark required props with .isRequired
  3. Provide default props where appropriate
  4. Use shape for objects to validate structure
  5. Create reusable types for common data structures


TypeScript Migration

When migrating to TypeScript, these PropTypes can be converted to TypeScript interfaces:

interface ModalProps {
isOpen: boolean;
onClose: () => void;
}

interface UserProps {
id?: string;
email?: string;
username?: string;
profilePhoto?: string;
}

Complete Type Definitions

import PropTypes from 'prop-types';

export const ModalType = PropTypes.shape({
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
});

export const UserType = PropTypes.shape({
id: PropTypes.string,
email: PropTypes.string,
username: PropTypes.string,
profilePhoto: PropTypes.string,
});