Function Standards
This document establishes coding standards for functions in the OX Agry enterprise agricultural application. Following these guidelines ensures consistency, maintainability, and optimal performance across the codebase.
Table of Contents
Function Types
Arrow Functions
Arrow functions maintain lexical this
binding and provide concise syntax for simple functions.
Use Case | Recommendation | Example |
---|---|---|
Functional Components | ✅ Preferred | const CropCard = ({ crop }) => { ... } |
Event Handlers | ✅ Preferred | const handleSubmit = (e) => { ... } |
Component-internal Functions | ✅ Preferred | const calculateTotal = () => { ... } |
useCallback Wrappers | ✅ Required | const memoizedFn = useCallback(() => { ... }, []) |
Regular Functions
Regular functions provide better readability for complex logic and support hoisting.
Use Case | Recommendation | Example |
---|---|---|
Pure Utility Functions | ✅ Preferred | function calculateYield(area, efficiency) { ... } |
Custom Hooks | ✅ Required | function useFieldData(fieldId) { ... } |
Complex Logic | ✅ Preferred | function optimizeCropRotation(fields) { ... } |
Generator Functions | ✅ Required | function* paginate(items) { ... } |
Export Patterns
Export Type | When to Use | Syntax | Import Syntax |
---|---|---|---|
Named Function Export | Utility functions, hooks | export function calculateArea() {} | import { calculateArea } from './utils' |
Named Arrow Function | Components, simple utilities | export const formatDate = () => {} | import { formatDate } from './formatters' |
Default Export | Main page components | export default function Page() {} | import Page from './Page' |
Multiple Exports | Related functionality | Multiple named exports in one file | import { fn1, fn2 } from './module' |
Component Organization
Functional Component Structure
Follow this order for organizing code within components:
- Imports
- Type definitions
- Component function declaration
- Hooks (useState, useEffect, etc.)
- Event handlers
- Derived/computed values
- Helper functions
- Render logic (return statement)
import React, { useState } from "react";
import { formatData } from "./utils";
// Type definitions
interface CropProps {
id: string;
name: string;
}
// Component declaration
export const CropItem = ({ id, name }: CropProps) => {
// Hooks
const [expanded, setExpanded] = useState(false);
// Event handlers
const handleToggle = () => {
setExpanded((prev) => !prev);
};
// Helper functions
const renderDetails = () => {
if (!expanded) return null;
return <div className="details">...</div>;
};
// Render logic
return (
<div className="crop-item">
<h3>{name}</h3>
<button onClick={handleToggle}>
{expanded ? "Hide" : "Show"} Details
</button>
{renderDetails()}
</div>
);
};
Best Practices
Naming Conventions
- Components: PascalCase (
CropViewer
,FieldMap
) - Hooks: camelCase with 'use' prefix (
useWeatherData
,useFieldSelection
) - Event Handlers: camelCase with 'handle' prefix (
handleSubmit
,handleCropSelect
) - Utility Functions: camelCase, descriptive verbs (
calculateYield
,formatDate
)
Performance Optimization
- Use
useCallback
for functions passed as props to prevent unnecessary re-renders - Use
useMemo
for expensive calculations - Extract pure utility functions outside of components
Function Parameters
- Use object destructuring for multiple parameters
- Use TypeScript interfaces for complex parameter types
- Provide default values where appropriate
Default Export Pattern
For default exports, we will standardize on the inline export pattern for consistency:
// Recommended: Inline default export function
export default function FieldDashboard({ fieldId }: Props) {
// Implementation
return <div>Dashboard for field {fieldId}</div>;
}
This approach offers several advantages:
- Cleaner, more concise syntax
- Immediate identification of the exported component/function
- Better support in some code editors for navigation
- Consistent pattern that's easy for all developers to follow
For React components specifically, use:
export default function ComponentName(props: ComponentProps) {
// Component implementation
}
For utility functions that need to be the default export:
export default function utilityName(param1: Type, param2: Type): ReturnType {
// Implementation
}
Important: Default exports should primarily be used for:
- Page components that represent a full route
- Main feature components that are the primary export of a file
- Configuration objects or specialized functions that are the primary purpose of a file
Code Examples
Component with Event Handlers
export const FieldSelector = ({ fields, onSelect }: FieldSelectorProps) => {
const handleFieldClick = (fieldId: string) => {
onSelect(fieldId);
};
return (
<div className="field-selector">
{fields.map((field) => (
<button key={field.id} onClick={() => handleFieldClick(field.id)}>
{field.name}
</button>
))}
</div>
);
};
Custom Hook
export function useCropData(cropId: string) {
const [data, setData] = useState<CropData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!cropId) return;
setLoading(true);
fetchCropData(cropId)
.then((result) => setData(result))
.catch((err) => setError(err))
.finally(() => setLoading(false));
}, [cropId]);
return { data, loading, error };
}
Utility Functions
// lib/agricultural-utils.ts
export function calculateFieldArea(length: number, width: number): number {
return length * width;
}
export function convertToAcres(hectares: number): number {
return hectares * 2.47105;
}
export function formatArea(area: number, unit: "ha" | "acre"): string {
return `${area.toFixed(2)} ${unit}`;
}
Performance Optimized Component
export const CropAnalytics = ({ cropId, historical }: CropAnalyticsProps) => {
// Memoized data processing function
const processData = useCallback(
(rawData: RawCropData) => {
// Complex data transformation logic
return transformedData;
},
[
/* dependencies */
]
);
// Memoized derived value
const summaryStats = useMemo(() => {
if (!cropData) return null;
return calculateSummaryStatistics(cropData);
}, [cropData]);
return <div className="analytics-container">{/* Component rendering */}</div>;
};