# Tooling and Ecosystem Standards for JavaScript
This document outlines the recommended standards and best practices for utilizing tooling and the ecosystem in JavaScript development. It aims to provide a clear and actionable guide for developers, promoting maintainability, performance, and security. These guidelines are tailored to the latest versions of JavaScript and related technologies.
## 1. Linting and Formatting
### Standard 1.1: Consistent Code Styling with ESLint and Prettier
**Do This:**
* Integrate ESLint with a well-defined configuration (e.g., Airbnb, Standard, Google) into your project.
* Utilize Prettier for automatic code formatting.
* Configure your editor and CI/CD pipeline to run ESLint and Prettier on every commit.
**Don't Do This:**
* Ignore linting errors or disable rules without careful consideration.
* Rely solely on manual code formatting.
* Skip linting and formatting steps in your CI/CD pipeline.
**Why:**
Consistent code styling improves readability and reduces cognitive load, leading to fewer errors and easier collaboration. Automated linting and formatting ensure that all code adheres to the defined style guide. Disabling rules without a clear justification can lead to inconsistencies and potential issues.
**Code Example (ESLint Configuration - .eslintrc.js):**
"""javascript
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"airbnb"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/prop-types": "off", // Disable prop-types validation
"no-unused-vars": "warn", // Treat unused variables as warnings
}
};
"""
**Code Example (Prettier Configuration - .prettierrc.js):**
"""javascript
module.exports = {
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 120,
tabWidth: 2,
};
"""
**Anti-Pattern:** Allowing codebases to diverge in style as different team members contribute using disparate personal settings. This destroys the advantages of using linters and formatters in the first place.
### Standard 1.2: Editor Integration
**Do This:** Install and enable ESLint and Prettier plugins for your code editor (VS Code, Sublime Text, etc.).
**Don't Do This:** Manually run linters/formatters. The editor plugin should do it automatically (on save).
**Why:** Editor integration leads to linting and formatting on save, providing immediate feedback and ensuring consistency as you code.
## 2. Package Management
### Standard 2.1: Choosing a Package Manager
**Do This:**
* Use npm (v7+), yarn (v1+), or pnpm for managing project dependencies.
* Consider pnpm for monorepos and workspaces due to its efficiency in disk space usage and speed.
**Don't Do This:**
* Mix different package managers within the same project.
* Commit "node_modules" to version control.
**Why:**
Package managers simplify dependency management, ensuring consistent versions across development environments. npm, yarn, and pnpm are all excellent choices, but pnpm offers advantages for complex projects and monorepos.
**Code Example (package.json):**
"""json
{
"name": "my-project",
"version": "1.0.0",
"description": "A sample project",
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"eslint": "^8.0.0",
"prettier": "^2.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint .",
"format": "prettier --write ."
},
"engines": {
"node": ">=16.0.0"
}
}
"""
**Anti-Pattern:** Using outdated package managers that are no longer supported or have known security vulnerabilities. Always keep npm/yarn/pnpm up-to-date.
### Standard 2.2: Semantic Versioning (SemVer)
**Do This:**
* Adhere to SemVer when publishing packages.
* Use version ranges ("^", "~", ">") in "package.json" to allow for compatible updates.
**Don't Do This:**
* Publish packages with unclear or inconsistent versioning.
* Use fixed versions (e.g., ""react": "18.2.0"") unless strictly necessary.
**Why:**
SemVer ensures predictable updates and minimizes the risk of breaking changes. Version ranges allow for automatic updates to compatible versions, keeping dependencies up-to-date while minimizing disruptions.
**Example "package.json" dependencies:**
"""json
"dependencies": {
"lodash": "^4.17.21", // Allows updates within the 4.x.x range
"axios": "~0.27.2", // Allows updates within the 0.27.x range
"moment": "2.29.4" // Pinning a specific known version
}
"""
### Standard 2.3: Dependency Auditing
**Do This:**
* Regularly run "npm audit", "yarn audit" or "pnpm audit" to identify and fix security vulnerabilities in dependencies.
* Automate dependency auditing in your CI/CD pipeline.
**Don't Do This:**
* Ignore audit findings.
* Manually apply security patches without testing.
**Why:**
Dependency auditing helps identify and mitigate security risks introduced by vulnerable dependencies. Regular audits and automated patching are essential for maintaining a secure application.
## 3. Testing Frameworks
### Standard 3.1: Unit and Integration Testing
**Do This:**
* Utilize testing frameworks like Jest, Mocha, or Jasmine.
* Write both unit and integration tests to ensure code functionality and interaction between modules.
* Aim for high code coverage (ideally >80%).
* Use mocking libraries like "jest.mock" or "sinon" for isolating units of code.
**Don't Do This:**
* Skip testing or write insufficient tests.
* Rely solely on manual testing.
* Ignore code coverage metrics.
**Why:**
Automated testing increases confidence in code quality, reduces bugs, and facilitates refactoring. Unit tests verify individual components, while integration tests ensure that the entire system works as expected. Aiming for high test coverage will allow you to catch more bugs. Additionally, testing provides documentation for how a part of the applications functions.
**Code Example (Jest Unit Test):**
"""javascript
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
"""
"""javascript
// sum.test.js
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
"""
**Code Example (Jest Mocking):**
"""javascript
// api.js
export const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
"""
"""javascript
// api.test.js
import { fetchData } from './api';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: 'mocked data' }),
})
);
test('fetchData returns data', async () => {
const data = await fetchData();
expect(data).toEqual({ data: 'mocked data' });
expect(fetch).toHaveBeenCalledTimes(1);
});
"""
**Anti-Pattern:** Writing tests that duplicate the implementation logic of the code being tested. Tests should focus on behavior, not implementation details.
### Standard 3.2: End-to-End (E2E) Testing & Component Testing
**Do This:**
* Use E2E testing frameworks like Cypress, Playwright, or Puppeteer for comprehensive application testing simulating real user interactions.
* Use component testing frameworks like Storybook to test components in isolation.
**Don't Do This:**
* Over-rely on manual testing for critical functionality.
* Neglect E2E testing for complex workflows or user journeys.
**Why:**
E2E tests simulate real user interactions, validating critical workflows and ensuring that the application functions as expected in a production-like environment. Component tests enable visual testing and ensure that components render correctly in the user interface.
**Code Example (Cypress E2E Test):**
"""javascript
// cypress/integration/example.spec.js
describe('My First Test', () => {
it('Visits the Kitchen Sink', () => {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
// Should be on a new URL which includes '/commands/actions'
cy.url().should('include', '/commands/actions')
// Get an input, type into it and verify that the value has been updated
cy.get('.action-email')
.type('fake@email.com')
.should('have.value', 'fake@email.com')
})
})
"""
## 4. Build Tools and Bundlers
### Standard 4.1: Modern JavaScript Bundlers
**Do This:**
* Utilize modern JavaScript bundlers like webpack, Parcel, esbuild or Rollup. Consider Vite for fast development builds.
* Configure your bundler to optimize code for production (minification, tree shaking, code splitting).
* Prefer esmodules over CommonJS modules (wherever possible).
* Configure separate builds for development and production environments.
**Don't Do This:**
* Rely on manual concatenation of JavaScript files.
* Include unnecessary code or dependencies in production builds.
**Why:**
Bundlers optimize code for production, reducing file sizes and improving loading performance. They also enable the use of modern JavaScript features and module systems. Tree shaking eliminates unused code which contributes to reduced file size. Properly configured webpack (or similar tool) builds are critical for achieving optimal performance.
**Code Example (webpack.config.js):**
"""javascript
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production', // 'development' or 'production'
entry: './src/index.js',
output: {
filename: 'bundle.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true, // Clean the output directory before each build
},
devtool: 'source-map', // Recommended for production debugging
optimization: {
minimize: true,
minimizer: [new TerserPlugin()], // Minimize JavaScript
splitChunks: { // Code splitting
chunks: 'all',
},
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true,
},
}),
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
};
"""
**Technology-Specific Detail:** When using webpack, leveraging its code-splitting features allows for loading only the necessary JavaScript for each route or component, greatly improving initial load times. Dynamic "import()" statements enable on-demand loading of modules.
**Anti-Pattern:** Failing to configure minification in production builds. This leaves unnecessary comments/whitespace in the final deliverable and increases file size.
### Standard 4.2: Transpilation
**Do This:**
* Use Babel to transpile modern JavaScript code (ES6+) to older versions for browser compatibility. Configure Babel using a ".babelrc.js" or "babel.config.js" file.
* Target specific browser versions using the "browserslist" configuration in "package.json".
**Don't Do This:**
* Skip transpilation, assuming all users will have modern browsers.
* Transpile unnecessarily, targeting overly old browsers.
**Why:**
Transpilation ensures that your code runs correctly in a wide range of browsers, including older versions. The "browserslist" configuration allows you to target specific browser versions, optimizing the transpilation process.
**Code Example (babel.config.js):**
"""javascript
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
browsers: ['>0.25%', 'not dead'], // Define target browsers with browserslist
},
useBuiltIns: 'usage', // Automatically include polyfills
corejs: 3,
}],
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties' // to support class properties
]
};
"""
**Code Example (package.json browserslist):**
"""json
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"maintained node versions"
]
"""
## 5. API Communication
### Standard 5.1: Fetch API or Axios
**Do This:**
* Use the built-in Fetch API (modern browsers) or Axios to make HTTP requests.
* Implement error handling using "try...catch" blocks and "response.ok" checks.
* Use async/await for cleaner and easier-to-read asynchronous code.
* Centralize API base URLs in configuration files.
**Don't Do This:**
* Rely on outdated libraries like "XMLHttpRequest" directly.
* Ignore potential errors during API calls.
**Why:**
Fetch API and Axios provide a modern and convenient way to make HTTP requests. Proper error handling and the use of async/await improve code readability and maintainability.
**Code Example (Fetch API):**
"""javascript
const API_BASE_URL = 'https://api.example.com';
async function fetchData(endpoint) {
try {
const response = await fetch("${API_BASE_URL}/${endpoint}");
if (!response.ok) {
throw new Error("HTTP error! status: ${response.status}");
}
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Re-throw the error for handling at a higher level
}
}
// Example usage:
fetchData('users')
.then(users => console.log(users))
.catch(error => console.error('Error fetching users:', error));
"""
**Code Example (Axios):**
"""javascript
import axios from 'axios';
const API_BASE_URL = 'https://api.example.com';
async function fetchData(endpoint) {
try {
const response = await axios.get("${API_BASE_URL}/${endpoint}");
return response.data;
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Re-throw the error for handling at a higher level
}
}
// Example usage:
fetchData('users')
.then(users => console.log(users))
.catch(error => console.error('Error fetching users:', error));
"""
**Anti-Pattern:** Hardcoding API keys directly in JavaScript code. This exposes the key on the client-side; instead, store keys securely on a backend server and only expose the necessary data through an API.
### Standard 5.2: API Versioning
**Do This**: Implement API versioning ("/api/v1/...", "/api/v2/...") to ensure backwards compatibility when API changes are introduced. Use feature flags to control the rollout of new API features.
**Why**: API versioning allows you to introduce breaking changes without impacting existing clients. Feature flags allow controlled and reversible feature releases.
## 6. State Management
### Standard 6.1: Choosing a State Management Library
**Do This:**
* Consider using state management libraries like Redux, Zustand, or Jotai, especially for complex applications with shared state.
* Choose a library that aligns with your project's needs and complexity. For simple cases, the Context API with "useReducer" might be sufficient.
* Keep application state minimal and derive values using memoization techniques ("useMemo" in React) as much as possible.
**Don't Do This:**
* Overuse state management libraries for simple applications.
* Mutate state directly without using proper update methods.
**Why:**
State management libraries provide a centralized and predictable way to manage application state, improving maintainability and reducing bugs. Choosing the right library depends on the project's complexity. For example, Zustand provides easy state management, while Redux is more structured and better suited for large, highly complex applications.
**Code Example (Redux):**
"""javascript
// store.js
import { createStore } from 'redux';
// Reducer function
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 };
case 'counter/decremented':
return { value: state.value - 1 };
default:
return state;
}
}
// Create a Redux store holding the state of your app.
const store = createStore(counterReducer);
export default store;
"""
"""javascript
// Counter Component
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const count = useSelector((state) => state.value);
const dispatch = useDispatch();
return (
{count}
dispatch({ type: 'counter/incremented' })}>Increment
dispatch({ type: 'counter/decremented' })}>Decrement
);
}
export default Counter;
"""
**Code Example (Zustand):**
"""javascript
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
function Counter() {
const { count, increment, decrement } = useStore()
return (
{count}
Increment
Decrement
)
}
export default Counter;
"""
**Technology-Specific Detail:** When using Redux with React, useSelector() is used extensively, which subscribes the component to just the specific slice of the Redux state that the component needs. This enhances component performance by avoiding unnecessary re-renders when unrelated parts of the state are updated.
### Standard 6.2: Immutability
**Do This:**
* Treat application state as immutable.
* Use immutable update patterns with object spread syntax or libraries like Immer.
**Don't Do This:**
* Directly mutate state objects.
**Why:**
Immutability makes state changes predictable and easier to debug. It also enables efficient change detection in frameworks like React.
**Code Example (Immutable Update with Spread Operator):**
"""javascript
const initialState = {
user: {
name: 'John Doe',
age: 30,
},
};
// Incorrect Mutating the state directly
// state.user.age = 31; // DO NOT DO THIS!
// Correct - Immutable update
const newState = {
...initialState,
user: {
...initialState.user,
age: 31,
},
};
"""
**Code Example (Immer):**
"""javascript
import { produce } from 'immer';
const initialState = {
user: {
name: 'John Doe',
age: 30,
},
};
const newState = produce(initialState, (draft) => {
draft.user.age = 31;
});
"""
## 7. Documentation Generation
### Standard 7.1: JSDoc Comments
**Do This:**
* Use JSDoc comments to document your code.
* Generate API documentation using tools like JSDoc or TypeDoc.
**Don't Do This:**
* Skip documentation or write unclear comments.
**Why:**
Well-documented code is easier to understand and maintain. JSDoc comments provide a standardized way to document your code, allowing tools to generate API documentation automatically.
**Code Example (JSDoc):**
"""javascript
/**
* Adds two numbers together.
* @param {number} a The first number.
* @param {number} b The second number.
* @returns {number} The sum of a and b.
*/
function sum(a, b) {
return a + b;
}
"""
## 8. Performance Monitoring and Debugging
### Standard 8.1: Browser Developer Tools
**Do This:**
* Utilize browser developer tools for debugging and performance analysis.
* Use the "console.log", "console.warn", and "console.error" statements judiciously for logging purposes.
* Use the Chrome DevTools performance tab to identify bottlenecks and optimize code.
**Don't Do This:**
* Leave excessive "console.log" statements in production code.
* Ignore performance issues identified by developer tools.
**Why:**
Browser developer tools provide valuable insights into code behavior, performance bottlenecks etc. Effective use of logging and debugging tools helps identify and resolve issues quickly.
### Standard 8.2: Error Tracking
**Do This:**
* Integrate error tracking services like Sentry or Bugsnag to capture and monitor runtime errors in production.
* Log meaningful error messages with context.
**Don't Do This:**
* Ignore runtime errors or rely solely on user reports.
**Why:**
Error tracking services enable proactive identification and resolution of runtime errors, improving application stability and user experience.
## 9. Security
### Standard 9.1: Input Validation and Output Encoding
**Do This:**
* Validate all user inputs to prevent security vulnerabilities like XSS and SQL injection.
* Encode outputs properly to prevent XSS attacks.
**Don't Do This:**
* Trust user input without validation.
* Directly insert user input into HTML or SQL queries.
**Why:**
Input validation and output encoding are essential for preventing security vulnerabilities. Sanitizing data before use can prevent malicious code from being executed.
### Standard 9.2: Dependency Security
**Do This:**
* Regularly audit dependencies for known security vulnerabilities using tools like "npm audit", "yarn audit", or "pnpm audit".
* Keep dependencies up-to-date with the latest security patches.
* Use a Software Bill of Materials (SBOM) to track the components in your application.
**Don't Do This:**
* Ignore security vulnerabilities identified in dependencies.
* Use outdated or unsupported dependencies.
**Why:**
Vulnerable dependencies can introduce security risks into your application. Regularly auditing dependencies and applying security patches helps mitigate these risks. An SBOM aids in identifying and remediating vulnerable components.
## 10. Continuous Integration and Continuous Deployment (CI/CD)
### Standard 10.1: Automated Builds and Tests
**Do This:**
* Set up a CI/CD pipeline using tools like Jenkins, GitHub Actions, GitLab CI or CircleCI.
* Automate builds, tests, and deployments.
* Run linting, formatting, and security checks in CI/CD.
**Don't Do This:**
* Rely on manual builds and deployments.
* Skip testing or security checks in CI/CD.
**Why:**
CI/CD automates the software development lifecycle, ensuring consistent builds, automated testing, and rapid deployments. This reduces the risk of errors and improves overall development velocity.
**Code Example (GitHub Actions):**
"""yaml
# .github/workflows/main.yml
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Format
run: npm run format
- name: Run tests
run: npm run test
- name: Build
run: npm run build
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: echo "Deploying to production..."
# Add deployment steps here (e.g., deploy to AWS, Azure, or Heroku)
"""
By adhering to these Tooling and Ecosystem standards, development teams can create maintainable, performant, and secure JavaScript applications. Continuous improvement and adaptation to new technologies are crucial for long-term success.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
Debe preferir usar el gestor de versiones "pnpm" por sobre "npm" para la ejecución de los comandos.
# Core Architecture Standards for JavaScript This document outlines the core architecture standards for JavaScript development. It's intended to guide developers and serve as context for AI coding assistants. The focus is on creating maintainable, performant, and secure JavaScript code. We will emphasize modern JavaScript practices and patterns. ## 1. Fundamental Architectural Patterns ### 1.1. Modular Architecture **Standard:** Structure applications into independent, reusable modules. **Why:** Improves code organization, testability, and reusability. Modules encapsulate functionality, reduce dependencies, prevent naming conflicts, and allow for easier maintenance and updates. **Do This:** Employ ES Modules (ESM) for native modularity. """javascript // myModule.js export function myFunction(param) { return param * 2; } export const myConstant = "Hello"; // main.js import { myFunction, myConstant } from './myModule.js'; console.log(myFunction(5)); // Output: 10 console.log(myConstant); // Output: Hello """ **Don't Do This:** Use global variables or pollute the global namespace. Avoid relying on script loading order. """javascript // Anti-pattern - global scope pollution function badFunction() { window.globalVariable = "Avoid this"; // Avoid } """ ### 1.2. Component-Based Architecture **Standard:** Design UIs as a composition of reusable components. **Why:** Promote code reuse, simplify testing, and improve UI maintainability. Components represent isolated parts of the application's UI. **Do This:** Utilize frameworks like React, Vue, or Angular. If building from scratch, establish clear component boundaries. Here's an example using React: """jsx // MyComponent.jsx import React from 'react'; function MyComponent({ name }) { return ( <div> <h1>Hello, {name}!</h1> </div> ); } export default MyComponent; // App.jsx import React from 'react'; import MyComponent from './MyComponent'; function App() { return ( <div> <MyComponent name="World" /> </div> ); } export default App; """ **Don't Do This:** Create monolithic, tightly coupled UI code. Avoid duplicating UI elements and logic. ### 1.3. Microservices (for backends) **Standard:** Decompose large applications into smaller, independently deployable microservices. **Why:** Allows for independent scaling, fault isolation, and technology diversity. Each microservice addresses a specific business capability. **Do This:** Design clear API boundaries (REST or GraphQL). Implement robust inter-service communication (e.g., asynchronous messaging via Kafka/RabbitMQ). Use containerization (Docker) and orchestration (Kubernetes). """javascript // Example (Conceptual) - Microservice A (User Service) // Implements API endpoints for user management // Example (Conceptual) - Microservice B (Order Service) // Implements API endpoints for order processing. Communicates with User Service to validate users. """ **Don't Do This:** Create tightly coupled services, share databases between services (unless absolutely necessary and carefully managed), or implement complex, monolithic APIs. ### 1.4. Event-Driven Architecture **Standard:** Design systems to react to events, enabling loose coupling and asynchronous communication. **Why:** Improves scalability, responsiveness, and resilience. Components interact by emitting and listening to events. **Do This:** Use message queues (RabbitMQ, Kafka), publish-subscribe patterns, or libraries like Node.js's "EventEmitter". Implement webhooks or server-sent events (SSE). """javascript // Example using Node.js EventEmitter const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', (data) => { console.log('Event occurred:', data); }); myEmitter.emit('event', { message: 'Hello from event emitter!' }); """ **Don't Do This:** Create tightly coupled event chains, ignore event handling errors, or rely solely on synchronous event processing for critical tasks. Avoid excessively broad event scopes. ## 2. Project Structure and Organization ### 2.1. Directory Structure **Standard:** Follow a consistent and well-defined project structure. **Why:** Improves project navigation, maintainability, and developer onboarding. **Do This:** Adopt a structure like: """ project-root/ ├── src/ # Source code │ ├── components/ # Reusable UI components │ │ ├── MyComponent/ │ │ │ ├── MyComponent.jsx │ │ │ ├── MyComponent.module.css # CSS Modules (Optional) │ │ │ └── index.js # Entry point (Optional) │ ├── pages/ # Application routes/pages │ │ ├── HomePage.jsx │ │ ├── AboutPage.jsx │ ├── services/ # API client logic │ │ ├── userService.js │ ├── utils/ # Utility functions │ │ ├── dateUtils.js │ ├── App.jsx # Main application component │ ├── index.js # Entry point for the application ├── public/ # Static assets (images, fonts) ├── config/ # Configuration files ├── scripts/ # Build and utility scripts ├── tests/ # Unit and integration tests ├── .eslintrc.js # ESLint configuration ├── package.json # Project dependencies and metadata └── README.md # Project documentation """ **Don't Do This:** Scatter files randomly, use inconsistent naming conventions, or create a deeply nested structure that's hard to navigate. ### 2.2. Naming Conventions **Standard:** Adhere to clear and consistent naming conventions. **Why:** Improves code readability and maintainability. **Do This:** * **Variables/Constants:** Use camelCase (e.g., "myVariable", "myConstant"). Use SCREAMING_SNAKE_CASE for truly immutable constants (e.g., "MAX_SIZE"). * **Functions:** Use camelCase (e.g., "getUserData", "calculateTotal"). * **Classes:** Use PascalCase (e.g., "MyClass", "UserData"). * **Components:** Use PascalCase (e.g., "MyComponent", "UserProfile"). Match filename to component name. * **Files:** Use kebab-case (e.g., "user-service.js", "my-component.jsx"). Match filename to exported function or component name. * **Directories:** Use kebab-case or PascalCase (for component directories). * **Booleans:** Start with "is", "has", or "should" (e.g., "isEnabled", "hasPermission", "shouldUpdate"). **Don't Do This:** Use ambiguous or cryptic names, use inconsistent casing, or use reserved keywords. ### 2.3. Code Style and Formatting **Standard:** Enforce a consistent code style across the project. **Why:** Improves code readability and collaboration. **Do This:** * Use "ESLint" with a predefined style guide (e.g., Airbnb, Google, Standard). * Use "Prettier" for automated code formatting. * Configure your IDE/editor to automatically format code on save. * Use single quotes for strings unless double quotes are needed for escaping. * Use 2 spaces for indentation. * Add a final newline to all files. """javascript // Example using ESLint and Prettier // .eslintrc.js module.exports = { "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 2020, "sourceType": "module" }, "env": { "browser": true, "node": true, "es6": true }, "rules": { "semi": ["error", "always"], "quotes": ["error", "single"], "no-unused-vars": "warn", "no-console": "warn" } }; // .prettierrc.js module.exports = { semi: true, trailingComma: 'all', singleQuote: true, printWidth: 120, tabWidth: 2, }; """ **Don't Do This:** Ignore code style guidelines, commit code with formatting errors, or use inconsistent indentation. ## 3. Modern JavaScript Features and Practices ### 3.1. Asynchronous Programming (async/await) **Standard:** Use "async/await" for cleaner asynchronous code. **Why:** Improves code readability and maintainability compared to callbacks or promises. **Do This:** """javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); throw error; // Re-throw to propagate the error } } fetchData() .then(data => console.log('Data:', data)) .catch(error => console.error('Caught error:', error)); // Added top-level catch in calling function """ **Don't Do This:** Mix callbacks and promises with "async/await", forget to handle errors with "try/catch", or use "async/await" unnecessarily in synchronous code. Also, place try/catch blocks within your async functions. Remember to handle rejected promises in the function that calls the async function in the event that the async function throws an error. ### 3.2. ES Modules **Standard:** Use ES Modules (ESM) for modularizing code. **Why:** Standardized module system with native browser support and better tooling. **Do This:** """javascript // myModule.js export function add(a, b) { return a + b; } // main.js import { add } from './myModule.js'; console.log(add(2, 3)); // Output: 5 """ **Don't Do This:** Use CommonJS modules ("require", "module.exports") in new projects unless specifically required by the environment (Node.js might still require for certain configuration files but try to avoid). Mix ESM and CommonJS without proper tooling. ### 3.3. Destructuring and Spread Syntax **Standard:** Use destructuring and spread syntax for concise and readable code manipulation. **Why:** Reduce boilerplate code and improve code clarity. **Do This:** """javascript // Destructuring const user = { name: 'John', age: 30, city: 'New York' }; const { name, age } = user; console.log(name, age); // Output: John 30 // Spread syntax const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; console.log(arr2); // Output: [1, 2, 3, 4, 5] const user2 = { ...user, occupation: 'Developer' }; console.log(user2); """ **Don't Do This:** Overuse destructuring to the point of reducing readability, or modify props directly when using the spread operator in React (create a new object instead). ### 3.4. Functional Programming Concepts **Standard:** Embrace functional programming concepts like immutability, pure functions, and higher-order functions. **Why:** Improve code testability, predictability, and reduce side effects. **Do This:** """javascript // Pure function function add(a, b) { return a + b; // No side effects } // Immutability const numbers = [1, 2, 3]; const newNumbers = numbers.map(num => num * 2); // Create a new array console.log(numbers); // Output: [1, 2, 3] console.log(newNumbers); // Output: [2, 4, 6] """ **Don't Do This:** Mutate data directly, rely on side effects, or create functions with hidden dependencies. ## 4. Security Best Practices ### 4.1. Input Validation **Standard:** Validate all user inputs on both client and server sides. **Why:** Prevent injection attacks (XSS, SQL injection), data corruption, and unexpected behavior. **Do This:** * Use a validation library (e.g., Joi, validator.js). * Sanitize inputs to remove potentially harmful characters. * Implement proper error handling for invalid inputs. """javascript // Example using Joi for input validation const Joi = require('joi'); const schema = Joi.object({ username: Joi.string().alphanum().min(3).max(30).required(), password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(), email: Joi.string().email({ tlds: { allow: ['com', 'net'] } }).required(), }); const userInput = { username: 'john_doe', password: 'securePassword', email: 'john.doe@example.com', }; const validationResult = schema.validate(userInput); if (validationResult.error) { console.error('Validation error:', validationResult.error.details); } else { console.log('Input is valid'); } """ **Don't Do This:** Trust user inputs implicitly, rely solely on client-side validation, or expose sensitive information in error messages. ### 4.2. Authentication and Authorization **Standard:** Implement secure authentication and authorization mechanisms. **Why:** Protect sensitive data and prevent unauthorized access. **Do This:** * Use a reputable authentication library (e.g., Passport.js, Auth0). * Store passwords securely using hashing algorithms (e.g., bcrypt). * Implement role-based access control (RBAC). * Use JSON Web Tokens (JWT) for stateless authentication. * Implement multi-factor authentication (MFA). """javascript // Example (Conceptual) - Authentication using JWT // Generating a JWT const jwt = require('jsonwebtoken'); const payload = { userId: 123, role: 'admin' }; const secretKey = 'mySecretKey'; // Store securely! const token = jwt.sign(payload, secretKey, { expiresIn: '1h' }); console.log('Generated token:', token); // Verifying a JWT jwt.verify(token, secretKey, (err, decoded) => { if (err) { console.error('Token verification failed:', err); } else { console.log('Decoded token:', decoded); } }); """ **Don't Do This:** Store passwords in plain text, use weak hashing algorithms, or implement custom authentication schemes without proper security expertise. ### 4.3. Cross-Site Scripting (XSS) Prevention **Standard:** Prevent cross-site scripting (XSS) attacks by properly escaping and sanitizing output. **Why:** Prevent malicious scripts from being injected into your application. **Do This:** * Escape HTML entities when rendering user-generated content. Libraries like DOMPurify are helpful. * Use Content Security Policy (CSP) headers to restrict the sources of scripts and other resources. """html <!-- Example CSP header --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' https://trusted-cdn.com; img-src 'self' data:"> """ **Don't Do This:** Render unescaped user input directly into the DOM, or allow inline JavaScript. ### 4.4. Cross-Site Request Forgery (CSRF) Prevention **Standard:** Prevent cross-site request forgery (CSRF) attacks by using anti-CSRF tokens. **Why:** Prevent malicious websites from making unauthorized requests on behalf of authenticated users. **Do This:** * Include a unique, unpredictable token in each form. * Verify the token on the server side before processing the request. * Use the "SameSite" attribute on cookies to prevent them from being sent with cross-origin requests. ## 5. Performance Optimization ### 5.1. Code Splitting **Standard:** Split your code into smaller chunks that can be loaded on demand. **Why:** Reduce initial load time and improve overall performance. **Do This:** * Use dynamic imports ("import()"). * Configure your bundler (Webpack, Parcel, Rollup) for code splitting. * Split code based on routes, components, or features. """javascript // Example using dynamic import async function loadComponent() { const { MyComponent } = await import('./MyComponent.js'); // Render MyComponent } """ **Don't Do This:** Load all code upfront, or create excessively small chunks that increase the number of HTTP requests. ### 5.2. Lazy Loading **Standard:** Load resources (images, components) only when they are needed. **Why:** Improve initial load time and reduce memory consumption. **Do This:** * Use "IntersectionObserver" to detect when an element is in the viewport. * Implement lazy loading for images using the "loading="lazy"" attribute (where supported) or JavaScript libraries. """html <img src="image.jpg" loading="lazy" alt="Lazy-loaded image"> """ """javascript //Example using Intersection Observer const lazyImages = document.querySelectorAll('.lazy-image'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if(entry.isIntersecting){ let lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; lazyImage.classList.remove('lazy-image'); observer.unobserve(lazyImage); } }); }); lazyImages.forEach(lazyImage => { observer.observe(lazyImage); }); """ **Don't Do This:** Load all resources upfront, or implement complex lazy loading logic for small resources. ### 5.3. Memoization **Standard:** Cache the results of expensive function calls. **Why:** Avoid redundant computations and improve performance. **Do This:** * Implement memoization using a simple cache object or a library like "memoize-one". * Use "React.memo" for memoizing React components. """javascript //Basic memoization technique function memoize(func){ const cache = {}; return function(...args){ const key = JSON.stringify(args); //Create a key from the arguments if(cache[key]){ return cache[key]; } const result = func(...args); cache[key] = result; return result; } } const expensiveFunction = (arg) => { console.log('computing...') return arg * 2; } const memoizedExpensiveFunction = memoize(expensiveFunction); console.log(memoizedExpensiveFunction(5)); //Output: computing... 10; console.log(memoizedExpensiveFunction(5)); //Output: 10 (cached) """ **Don't Do This:** Memoize functions unnecessarily, or use a cache that grows unbounded. Also make sure to select appropriate keys and implement correct cache invalidation if applicable. ### 5.4. Optimize Loops and DOM Manipulations **Standard:** Write efficient loops and minimize DOM manipulations. **Why:** Improve runtime performance and reduce browser reflows. **Do This:** * Use "for" or "while" loops instead of "forEach" for performance-critical sections. * Cache DOM element references. * Use "DocumentFragment" to perform multiple DOM manipulations at once. """javascript // Example using DocumentFragment const fragment = document.createDocumentFragment(); const list = document.getElementById('myList'); for (let i = 0; i < 100; i++) { const li = document.createElement('li'); li.textContent = "Item ${i}"; fragment.appendChild(li); } list.appendChild(fragment); // Single DOM manipulation """ **Don't Do This:** Perform excessive DOM manipulations in a loop, or use inefficient loop constructs. ### 5.5 Use correct data structures **Standard:** Utilize correct data structures given the problem context. Why: Improved data operation efficiency and improves code clarity. Do This: * Use "Maps" when needing to preserve insertion order or if keys are complex data types. * Use "Sets" when you need to work with unique values and fast membership checks. * Use "Arrays" for ordered collections of values, especially where index-based access matters. """javascript // Using Maps const myMap = new Map(); myMap.set('name', 'John'); myMap.set(1, 'Number One'); console.log(myMap.get('name')); // Output: John console.log(myMap.get(1)); // Output: Number One // Using Sets const mySet = new Set(); mySet.add(1); mySet.add(5); mySet.add(5); // Add duplicate item console.log(mySet.has(1)); // Output: true console.log(mySet.size); // Output: 2 """ Don't Do This: Don't default always to using plain objects or arrays. Consider the advantages that modern data structures like Maps and Sets bring. This document provides a solid foundation for establishing JavaScript coding standards focusing on core architecture. It's important to adapt and extend these guidelines to suit the specific needs and context of your project. Consistent application of these standards will lead to more maintainable, performant, and secure JavaScript applications.
# Code Style and Conventions Standards for JavaScript This document outlines the coding style and conventions standards for JavaScript development, aiming to promote consistency, readability, and maintainability across all projects. These standards leverage modern JavaScript (ES2024 and beyond) features and best practices. This document is intended to guide developers and inform AI coding assistants. ## 1. General Principles ### 1.1. Consistency * **Do This:** Adhere strictly to the guidelines outlined in this document. * **Don't Do This:** Introduce personal stylistic preferences that deviate from these standards unless justified by a specific technical need and approved by the team. **Why:** Consistent code is easier to read, understand, and maintain. It reduces cognitive load and makes collaboration more efficient. ### 1.2. Readability * **Do This:** Write code that is easily understandable by other developers (and your future self). Use meaningful variable names, consistent indentation, and clear comments. Prioritize clarity over brevity. * **Don't Do This:** Write overly complex or obfuscated code. Avoid clever tricks that sacrifice readability for minimal performance gains. **Why:** Readability is paramount. Well-readable code reduces the likelihood of errors and makes debugging and maintenance faster. ### 1.3. Maintainability * **Do This:** Design code that is modular, testable, and easy to modify or extend. Follow established design principles like DRY (Don't Repeat Yourself) and SOLID. * **Don't Do This:** Create tightly coupled, monolithic code that is difficult to refactor or reuse. **Why:** Maintainable code reduces the long-term cost of ownership. It allows for efficient bug fixes, feature additions, and technology upgrades. ### 1.4. Use of Modern JavaScript Features * **Do This:** Prefer modern ES2024 (and beyond) features such as "async/await", arrow functions, "const" and "let", class syntax, destructuring, spread syntax, and optional chaining. * **Don't Do This:** Rely on older, less readable syntax like "var", "function" keyword when arrow functions are appropriate, and verbose conditional checks. **Why:** Modern JavaScript features often provide more concise, readable, and performant solutions. They also improve code safety and reduce the likelihood of errors. ## 2. Formatting ### 2.1. Indentation * **Do This:** Use 2 spaces for indentation. Avoid tabs. * **Don't Do This:** Mix spaces and tabs or use inconsistent indentation. **Why:** Consistent indentation improves readability and visual structure. Two spaces are widely accepted and provide a good balance between nesting visibility and line length. """javascript // Correct indentation function processData(data) { if (data) { console.log("Data:", data); } } // Incorrect indentation function processData(data) { if (data) { console.log("Data:", data); } } """ ### 2.2. Line Length * **Do This:** Limit lines to a maximum of 80-120 characters. Break long lines for readability. * **Don't Do This:** Allow lines to exceed the maximum length, making code difficult to read on smaller screens or IDEs. **Why:** Reasonable line length keeps code readable and prevents horizontal scrolling, especially when viewing code side-by-side or in a code review tool. """javascript // Correct line length const formattedMessage = "This is a long message that wraps to the next line to improve readability and maintain a reasonable line length."; // Incorrect line length const formattedMessage = "This is a very long message that does not wrap and exceeds the recommended line length making it difficult to read."; """ ### 2.3. Whitespace * **Do This:** Use whitespace strategically to improve readability: * Add a space after commas and colons. * Add a space around operators (e.g., "=", "+", "-", "*", "/"). * Add blank lines to separate logical blocks of code. * Add a newline at the end of each file. * **Don't Do This:** Use excessive or inconsistent whitespace, which can make code harder to scan. **Why:** Consistent spacing improves visual clarity and makes code easier to parse.. """javascript // Correct whitespace const sum = a + b; const person = { name: "John", age: 30 }; if (isValid) { console.log("Valid"); } // Incorrect whitespace const sum=a+b; const person={name:"John",age:30}; if(isValid){ console.log("Valid"); } """ ### 2.4. Braces * **Do This:** Use braces for all control flow statements (e.g., "if", "else", "for", "while") even if the block contains only one statement. Place the opening brace on the same line as the statement. * **Don't Do This:** Omit braces for single-line blocks, as this can lead to errors and reduce readability. **Why:** Explicit braces improve readability and prevent potential errors when adding statements to a block. """javascript // Correct braces if (isValid) { console.log("Valid"); } else { console.log("Invalid"); } // Incorrect braces if (isValid) console.log("Valid"); // Avoid this else console.log("Invalid"); // Avoid this """ ### 2.5. Semicolons * **Do This:** Always use semicolons to terminate statements. Relying on automatic semicolon insertion (ASI) can lead to unexpected behavior. * **Don't Do This:** Omit semicolons at the end of statements, especially assignment ones. **Why:** Explicit semicolons ensure that statements are interpreted correctly and prevent potential errors caused by ASI. """javascript // Correct semicolons const message = "Hello"; console.log(message); // Incorrect semicolons (potential issue with ASI) const message = "Hello" console.log(message) """ ## 3. Naming Conventions ### 3.1. General Naming * **Do This:** * Use descriptive and meaningful names. * Avoid single-letter variable names (except in simple loop counters). * Use camelCase for variables and function names. * Use PascalCase for class names and constructor functions. * Use SCREAMING_SNAKE_CASE for constants. * **Don't Do This:** * Use ambiguous or abbreviated names. * Use names that conflict with reserved keywords. * Use inconsistent naming conventions. **Why:** Clear and consistent naming improves readability and makes it easier to understand the purpose of variables, functions, and classes. """javascript // Correct naming const userName = "John Doe"; function calculateTotal(price, quantity) { return price * quantity; } class Product { constructor(name, price) { this.name = name; this.price = price; } } const MAX_VALUE = 100; // Incorrect naming const a = "John Doe"; // Avoid function calc(p, q) { // Avoid return p * q; } """ ### 3.2. Variable Naming * **Do This:** Use noun or noun phrase for variables. * **Don't Do This:** Use verbs in variable names (unless they are boolean variables). **Why:** Variable names should clearly indicate what the variable *represents*. """javascript // Correct variable naming const userAge = 30; const productList = []; let isValidUser = true; // Boolean // Incorrect variable naming let calculate = 10; // Avoid - use a noun let adding = true; // Avoid - use 'isAdding' if boolean """ ### 3.3. Function Naming * **Do This:** Use verb or verb phrase for function names. * **Don't Do This:** Use nouns in function names. **Why:** Function names should clearly indicate what the function *does*. """javascript // Correct function naming function calculateArea(width, height) { return width * height; } function isValidEmail(email) { //Email validation logic return true; } // Incorrect function naming function area(width, height) { // Vague, not descriptive return width * height; } """ ### 3.4. Boolean Variables and Functions * **Do This:** Prefix boolean variable and function names with "is", "has", or "are". * **Don't Do This:** Use ambiguous names that do not clearly indicate a boolean value. **Why:** Consistent naming for boolean variables improves readability and reduces ambiguity. """javascript // Correct boolean naming let isValid = true; function hasPermission(user) { return user.permissions.includes("admin"); } // Incorrect boolean naming let valid = true; // Avoid function permission(user) { // Avoid return user.permissions.includes("admin"); } """ ## 4. Stylistic Consistency ### 4.1. Quotes * **Do This:** Use single quotes ("'") for string literals unless the string contains single quotes itself, in which case use template literals or double quotes ("""). * **Don't Do This:** Mix single and double quotes inconsistently. **Why:** Consistency improves readability. Single quotes are slightly easier to type and are generally preferred. Template literals offer additional functionality like string interpolation. """javascript // Correct quotes const name = 'John Doe'; const message = "He said, 'Hello!'"; const greeting = "Hello, ${name}!"; const path = 'c:\\path\\to\\file'; // Incorrect quotes const name = "John Doe"; // Inconsistent """ ### 4.2. Arrow Functions vs. Function Declarations * **Do This:** Prefer arrow functions ("=>") for concise, non-method functions. Use function declarations ("function") for named functions and methods within classes. * **Don't Do This:** Use arrow functions for complex functions with multiple statements or for methods that require "this" to refer to the class instance. **Why:** Arrow functions provide a more concise syntax and lexically bind "this". Function declarations are more suitable for named functions and methods that require "this" to refer to the object. """javascript // Correct arrow functions and function declarations const add = (a, b) => a + b; // Concise arrow function function multiply(a, b) { // Named function return a * b; } class Calculator { add(a, b) { // Method using function declaration return a + b; } } // Incorrect use of arrow function const calculator = { value:0, add: (a) => {this.value += a;} // WRONG: this will not refer to calculator } """ ### 4.3. Object and Array Literals * **Do This:** Use concise object and array literals: * Use shorthand property names (e.g., "{ name }" instead of "{ name: name }"). * Use spread syntax ("...") to copy or merge objects and arrays. * **Don't Do This:** Use verbose or outdated syntax for creating objects and arrays. **Why:** Concise literals improve readability and reduce boilerplate code. """javascript // Correct object and array literals const name = "John Doe"; const age = 30; const person = { name, age }; // Shorthand property names const numbers = [1, 2, 3]; const moreNumbers = [...numbers, 4, 5]; // Spread syntax // Incorrect object and array literals const personOld = { name: name, age: age }; // Verbose const moreNumbersOld = numbers.concat([4,5]); // Inefficient """ ### 4.4. Ternary Operators * **Do This:** Use ternary operators ("condition ? value1 : value2") for simple conditional assignments. * **Don't Do This:** Use ternary operators for complex logic or nested conditions, as this can reduce readability. Prefer "if/else" statements for complex conditions. **Why:** Ternary operators provide a concise syntax for simple conditions. However, complex ternary expressions can be difficult to understand and maintain. """javascript // Correct ternary operator const status = isValid ? "Active" : "Inactive"; // Incorrect ternary operator (too complex) const result = (a > b) ? (a > c ? a : c) : (b > c ? b : c); // Difficult understand. Avoid this! """ ## 5. Specific Code Examples and Patterns ### 5.1. Asynchronous Programming with "async/await" * **Do This:** Prefer "async/await" over callbacks and promises for asynchronous operations. * **Don't Do This:** Use deeply nested callbacks (callback hell) or neglect error handling in asynchronous code. **Why:** "async/await" makes asynchronous code easier to read and reason about. It also simplifies error handling with "try/catch" blocks. """javascript // Correct async/await async function fetchData() { try { const response = await fetch("https://api.example.com/data"); const data = await response.json(); return data; } catch (error) { console.error("Error fetching data:", error); throw error; // Re-throw the error to be handled upstream } } // Incorrect callbacks example (callback hell) function fetchDataOld(callback) { fetch("https://api.example.com/data") .then(response => response.json()) .then(data => { callback(data); }) .catch(error => { //Note: this will not handle errors thrown _inside_ the callback. console.error("Error fetching data:", error); }); } """ ### 5.2. Error Handling * Compile-time validation errors that might arise due to coding mistakes, must be handled gracefully and informative error messages should be displayed to the end-user. * **Do This:** Implement robust error handling using "try/catch" blocks and "finally" blocks. Handle errors at the appropriate level, either by logging them, displaying user-friendly messages, or re-throwing them for higher-level handling. * **Don't Do This:** Ignore errors or re-throw them without providing context. Don't rely solely on unhandled promise rejections. **Why:** Proper error handling prevents unexpected crashes and provides valuable information for debugging and maintenance. """javascript // Correct error handling async function processData(input) { try { // Validate input if (!input) { throw new Error("Input cannot be null or undefined."); } // Some code that can throw error... const result = await someAsyncOperation(input); return result; } catch (error) { console.error("Error processing data:", error.message); // Re-throw the error for higher-level handling throw new Error("Failed to process input data.", { cause: error }); } finally { // Clean up resources. This will always execute even if the try block throws. } } // Incorrect error handling async function processDataBad(input) { const result = await someAsyncOperation(input) // What if it throws? No try/catch. return result; //We might not reach here...but no error reporting! } """ ### 5.3. Module Imports and Exports * **Do This:** Use ES module syntax ("import" and "export") for organizing code into reusable modules. * **Don't Do This:** Use CommonJS ("require" and "module.exports") unless required maintaining compatibility with older environments. **Why:** ES modules provide a standardized and more efficient way to organize and load code compared to CommonJS. """javascript // Correct ES modules // file: utils.js export function add(a, b) { return a + b; } export const PI = 3.14159; // file: main.js import { add, PI } from "./utils.js"; console.log(add(2, 3)); console.log(PI); // Incorrect CommonJS // utils.js // module.exports = { // add: function(a, b) { return a + b; }, // PI: 3.14159 // }; // main.js // const utils = require('./utils'); // console.log(utils.add(2, 3)); // console.log(utils.PI); """ ### 5.4. Immutability * **Do This:** Favor immutability, especially when working with data transformations. Use methods like "map", "filter", "reduce", and the spread syntax to create new objects and arrays instead of modifying existing ones directly. * **Don't Do This:** Mutate objects and arrays directly, which can lead to unexpected side effects and make debugging more difficult. **Why:** Immutability makes code easier to reason about and test. It also prevents unintended side effects and enables more efficient change detection. """javascript // Correct immutability const numbers = [1, 2, 3]; const squaredNumbers = numbers.map(num => num * num); // Create a new array const newNumbers = [...numbers, 4]; // Create a new array // Incorrect mutation const numbersBad = [1, 2, 3]; numbersBad.push(4); // Mutates the original array! Avoid! """ ### 5.5. Use of "const" and "let" * **Do This:** Use "const" for variables that should not be reassigned after their initial value. Use "let" for variables that need to be reassigned. Avoid using "var". * **Don't Do This:** Use "var" in modern JavaScript code. Use "let" when "const" is more appropriate. **Why:** "const" and "let" provide block scope and improve code clarity. Using "const" signals that a variable's value should not change, which helps prevent accidental reassignments. "var" has function scope, which can lead to unexpected behavior. """javascript // Correct use of const and let const userName = "John"; // Value should not change let age = 30; // Value may change age = 31; // OK // Incorrect use of var var message = "Hello"; // Avoid function example() { var x = 10; if (true) { var x = 20; // Oops! Overwrites the outer x (function scope) console.log(x); // 20 } console.log(x); // 20 (unexpected) } example(); """ ### 5.6 Classes and Object-Oriented Programming * **Do This:** Use ES6 class syntax ("class") for creating objects and using object oriented-programming paradigms. * **Don't Do This:** Use older approaches to creating class-like structures. **Why:** ES6 Classes provide a clean syntax and structure for creating objects. """javascript // Correct use of Classes class Animal { constructor(name, sound) { this.name = name; this.sound = sound; } makeSound() { console.log(this.sound); } } const dog = new Animal('dog','woof'); dog.makeSound(); """ ### 5.7 Documentation and Comments * **Do This:** Add comments to explain complex logic, non-obvious code, and the purpose of functions and modules. Use JSDoc syntax liberally. * **Don't Do This:** Write excessive comments that simply reiterate what the code already does. """javascript /** * Calculates the area of a rectangle. * @param {number} width The width of the rectangle. * @param {number} height The height of the rectangle. * @returns {number} The area of the rectangle. */ function calculateArea(width, height) { // Multiply width and height to get the area return width * height; } """ ### 5.8 Testing * **Do This:** Write unit tests for every function and class. Aim for high code coverage. * **Don't Do This:** Skip testing or write superficial tests that don't thoroughly validate the code. """javascript // Example with Jest test('adds 1 + 2 to equal 3', () => { expect(1 + 2).toBe(3); }); """ ### 5.9 Security Practices * **Do This:** Sanitize user inputs. Use HTTPS. Prevent Cross-Site Scripting and SQL Injection attacks. Regularly update dependencies for security patches. * **Don't Do This:** Store sensitive information in client-side code. Neglect security best practices. These coding standards are designed to ensure the JavaScript code is consistent, readable, and maintainable. Adhering to these guidelines will make collaboration easier and reduce the likelihood of errors. All developers should follow these conventions and continually strive to improve the quality of code.
# Component Design Standards for JavaScript This document outlines the component design standards for JavaScript, focusing on creating reusable, maintainable, and performant components. It leverages modern JavaScript features and best practices to ensure high-quality code. ## 1. Component Abstraction and Reusability ### 1.1 Standard: Encapsulation and Information Hiding **Do This:** * Encapsulate component logic and state within the component. * Use closures or WeakMaps to hide internal implementation details and prevent external modification. * Expose only a minimal, well-defined public API. **Don't Do This:** * Directly modify internal component state from outside. * Expose unnecessary implementation details in the public API. **Why:** Encapsulation reduces dependencies and makes components easier to refactor and reuse without affecting other parts of the application. **Example:** """javascript // Good: Using closures for encapsulation const createCounter = () => { let count = 0; return { increment: () => { count++; return count; }, decrement: () => { count--; return count; }, getCount: () => count }; }; const counter = createCounter(); console.log(counter.increment()); // 1 console.log(counter.decrement()); // 0 console.log(counter.getCount()); // 0 // Attempting to directly modify "count" will not work // counter.count = 10; // This has no effect """ """javascript // Bad: Exposing internal state directly const Counter = () => { this.count = 0; this.increment = () => { this.count++; return this.count; }; this.decrement = () => { this.count--; return this.count; }; }; const counterInstance = new Counter(); console.log(counterInstance.increment()); // 1 counterInstance.count = 100; // Direct modification console.log(counterInstance.count); // 100 """ ### 1.2 Standard: Single Responsibility Principle (SRP) **Do This:** * Design components that have one, and only one, reason to change. * Break down complex components into smaller, focused components. **Don't Do This:** * Create "god components" that handle multiple unrelated responsibilities. **Why:** SRP simplifies component design, making them easier to understand, test, and maintain. **Example:** """javascript // Good: Separating concerns // Component: Displays user profile information const UserProfile = ({ user }) => { return ( <div> <h2>{user.name}</h2> <p>Email: {user.email}</p> <UserAddress address={user.address} /> </div> ); }; // Component: Displays user address const UserAddress = ({ address }) => { return ( <div> <h3>Address:</h3> <p>{address.street}</p> <p>{address.city}, {address.state} {address.zip}</p> </div> ); }; """ """javascript // Bad: Mixing concerns // Component: Displays user profile and address (bad SRP) const UserDetails = ({ user }) => { return ( <div> <h2>{user.name}</h2> <p>Email: {user.email}</p> <h3>Address:</h3> <p>{user.address.street}</p> <p>{user.address.city}, {user.address.state} {user.address.zip}</p> </div> ); }; """ ### 1.3 Standard: Parameterization and Configuration **Do This:** * Make components configurable through props or configuration objects. * Provide sensible defaults for optional parameters. * Use TypeScript interfaces or JSDoc to define the expected props. **Don't Do This:** * Hardcode values that should be configurable. * Rely on global state or side effects for component behavior. **Why:** Parameterization increases the flexibility and reusability of components across different contexts. **Example:** """javascript // Good: Configuring component via props /** * @typedef {Object} ButtonProps * @property {string} label - The text displayed on the button. * @property {string} [type='button'] - The button type (e.g., 'submit', 'reset'). * @property {function} onClick - The function to execute when the button is clicked. */ /** * Creates a button component. * @param {ButtonProps} props - The button properties. * @returns {HTMLButtonElement} - The button element. */ const Button = ({ label, type = 'button', onClick }) => { const handleClick = () => { onClick(); }; const buttonElement = document.createElement('button'); buttonElement.textContent = label; buttonElement.type = type; buttonElement.addEventListener('click', handleClick); return buttonElement; }; // Usage const myButton = Button({ label: 'Click Me', type: 'submit', onClick: () => console.log('Button clicked!') }); document.body.appendChild(myButton); """ """javascript // Bad: Hardcoding values const Button = () => { const buttonElement = document.createElement('button'); buttonElement.textContent = 'Click Me'; // Hardcoded buttonElement.type = 'button'; // Hardcoded buttonElement.addEventListener('click', () => { console.log('Button clicked!'); // Hardcoded }); return buttonElement; }; document.body.appendChild(Button()); """ ### 1.4 Standard: Avoid Global State and Side Effects **Do This:** * Design components as pure functions or with minimal side effects. * Manage state using dedicated state management libraries or component-local state. * Isolate side effects within specific lifecycle methods or effect hooks. **Don't Do This:** * Directly modify global variables or external state from within components. * Perform uncontrolled side effects that can impact other parts of the application. **Why:** Minimizing side effects makes components more predictable, testable, and easier to reason about. **Example:** """javascript // Good: Local component state import { useState } from 'react'; const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; """ """javascript // Bad: Global state let globalCount = 0; const Counter = () => { const increment = () => { globalCount++; console.log('Global Count:', globalCount); }; return ( <div> <p>Count: {globalCount}</p> <button onClick={increment}>Increment</button> </div> ); }; """ ## 2. Component Composition and Patterns ### 2.1 Standard: Favor Composition over Inheritance **Do This:** * Build complex components by composing smaller, reusable components. * Use strategies like function composition or higher-order components to enhance functionality. **Don't Do This:** * Rely heavily on inheritance hierarchies, which can lead to tightly coupled and inflexible code. **Why:** Composition promotes loose coupling and allows for more flexible and dynamic component structures. **Example:** """javascript // Good: Composition const WithLogging = (WrappedComponent) => { return (props) => { console.log("Component ${WrappedComponent.name} is rendering"); return <WrappedComponent {...props} />; }; }; const MyComponent = () => { return <p>Hello from MyComponent!</p>; }; const LoggedComponent = WithLogging(MyComponent); """ """javascript // Bad: Inheritance (example in ES6 classes if required) class BaseComponent extends React.Component { logRendering() { console.log("Component is rendering"); } render() { this.logRendering(); return null; } } class MyComponent extends BaseComponent { render() { super.render(); return <p>Hello from MyComponent!</p>; } } """ ### 2.2 Standard: Render Props and Hooks **Do This:** * Use render props or hooks to share logic between components. * Create custom hooks to encapsulate reusable stateful logic. **Don't Do This:** * Duplicate logic across multiple components. **Why:** Render props and hooks allow for clean and reusable logic sharing, improving component maintainability. **Example:** """javascript // Good: Custom Hook import { useState, useEffect } from 'react'; const useWindowSize = () => { const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight, }); useEffect(() => { const handleResize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); return windowSize; }; const MyComponent = () => { const { width, height } = useWindowSize(); return ( <div> <p>Window Width: {width}</p> <p>Window Height: {height}</p> </div> ); }; """ ### 2.3 Standard: Design Patterns (e.g., Observer, Factory) **Do This:** * Implement appropriate design patterns to solve common component design problems. * Document the pattern usage clearly. **Don't Do This:** * Overuse patterns or apply them where simpler solutions are adequate. **Why:** Design patterns provide proven solutions to recurring problems, improving code structure and maintainability. **Example - Observer Pattern:** """javascript // Good: Observer Pattern class Subject { constructor() { this.observers = []; } subscribe(observer) { this.observers.push(observer); } unsubscribe(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } update(data) { console.log("${this.name} received data: ${data}"); } } const subject = new Subject(); const observer1 = new Observer("Observer 1"); const observer2 = new Observer("Observer 2"); subject.subscribe(observer1); subject.subscribe(observer2); subject.notify("Hello, Observers!"); // Output to console from both observers """ ## 3. Component Styling and Theming ### 3.1 Standard: CSS-in-JS or CSS Modules **Do This:** * Use CSS-in-JS libraries or CSS Modules for component styling, encapsulating styles within the component. * Avoid global CSS styles that can cause conflicts. **Don't Do This:** * Use inline styles excessively, as they are difficult to maintain and reuse. **Why:** CSS-in-JS and CSS Modules improve component encapsulation and prevent style conflicts. **Example: Styled Components** """javascript // Good: Using Styled Components (CSS-in-JS) import styled from 'styled-components'; const StyledButton = styled.button" background-color: #4CAF50; border: none; color: white; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; &:hover { background-color: #3e8e41; } "; const MyComponent = () => { return <StyledButton>Click me</StyledButton>; }; """ ### 3.2 Standard: Theming and Variable Usage **Do This:** * Use a theming solution (e.g., CSS variables, styled-components themes) to manage consistent styling across the application. * Define and use semantic variable names. **Don't Do This:** * Hardcode color values or font sizes throughout the codebase. **Why:** Theming improves consistency and simplifies style updates. **Example:** """javascript // Good: Using CSS Variables for Theming :root { --primary-color: #007bff; --secondary-color: #6c757d; --font-size-base: 16px; } .button { background-color: var(--primary-color); color: white; font-size: var(--font-size-base); padding: 10px 20px; border: none; } .button.secondary { background-color: var(--secondary-color); } """ ## 4. Component Testing ### 4.1 Standard: Unit Testing **Do This:** * Write unit tests for individual components to verify their behavior in isolation. * Use mocking or stubbing to isolate components from external dependencies. **Don't Do This:** * Skip unit tests for complex components. * Write tests that are too tightly coupled to the implementation details. **Why:** Unit tests ensure that components function correctly and prevent regressions during refactoring. **Example using Jest and React Testing Library:** """javascript // Good: Unit Test for a Counter Component import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Counter from './Counter'; test('increments counter when increment button is clicked', () => { const { getByText } = render(<Counter />); const incrementButton = getByText('Increment'); const countElement = getByText('Count: 0'); fireEvent.click(incrementButton); expect(getByText('Count: 1')).toBeInTheDocument(); }); """ ### 4.2 Standard: Integration Testing **Do This:** * Write integration tests to verify the interaction between multiple components. * Test the component within the context of its surroundings **Don't Do This:** * Only focus on unit tests and neglect integration tests **Why:** Integration tests ensure that components work together as expected and that the application functions correctly as a whole. ## 5. Component Performance ### 5.1 Standard: Memoization **Do This:** * Use "React.memo" or similar techniques to prevent unnecessary re-renders of components that receive the same props. * Consider using Reselect for memoizing derived data from state. **Don't Do This:** * Memoize components indiscriminately, as the memoization check itself has a cost. **Why:** Memoization can significantly improve performance by reducing the number of unnecessary renders. **Example:** """javascript // Good: memoization of a component import React from 'react'; const MyComponent = React.memo(({ data }) => { console.log('MyComponent is rendering'); // Only renders when props change return <p>Data: {data}</p>; }); export default MyComponent; """ ### 5.2 Standard: Lazy Loading **Do This:** * Use lazy loading to load components only when they are needed. * Implement code splitting to reduce the initial bundle size. **Don't Do This:** * Load all components upfront, which can slow down initial page load times. **Why:** Lazy loading improves initial load times and reduces the amount of code that needs to be downloaded by the browser. **Example:** """javascript // Good: Lazy Loading a Component import React, { lazy, Suspense } from 'react'; const LazyComponent = lazy(() => import('./LazyComponent')); const MyPage = () => { return ( <div> <h1>My Page</h1> <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> </div> ); }; export default MyPage; """ ### 5.3 Standard: Virtualization **Do This:** * Use virtualization techniques (e.g., "react-window", "react-virtualized") for rendering large lists or tables. * Avoid rendering all elements in the list at once, which can cause performance issues. **Don't Do This:** * Render large lists or tables without virtualization. **Why:** Virtualization significantly improves performance when rendering large lists by only rendering the visible elements. ## 6. Accessibility ### 6.1 Standard: ARIA Attributes **Do This:** * Use ARIA attributes to provide semantic information about components for assistive technologies. * Ensure that components are keyboard accessible. **Don't Do This:** * Rely solely on visual cues to convey information. **Why:** ARIA attributes make components accessible to users with disabilities. **Example:** """javascript // Good: Using ARIA Attributes const AccessibleButton = ({ onClick, children }) => { return ( <button onClick={onClick} aria-label="Click to perform action"> {children} </button> ); }; """ ### 6.2 Standard: Semantic HTML **Do This:** * Use semantic HTML elements (e.g., "<article>", "<nav>", "<aside>") to structure components. * Provide appropriate labels and descriptions for form elements. **Don't Do This:** * Use "<div>" or "<span>" elements excessively without providing semantic meaning. **Why:** Semantic HTML improves accessibility and SEO by providing meaningful structure to the content. ## 7. Security Considerations ### 7.1 Standard: Input Validation and Sanitization **Do This:** * Validate and sanitize all user inputs to prevent script injection. * Use appropriate encoding techniques for displaying user-generated content. **Don't Do This:** * Directly render user-provided strings without sanitization. **Why:** Input validation and sanitization are crucial for preventing security vulnerabilities such as cross-site scripting (XSS). **Example:** """javascript // Escape HTML function escapeHTML(str) { let div = document.createElement('div'); div.appendChild(document.createTextNode(str)); return div.innerHTML; } const unsafeText = '<script>alert("XSS");</script>'; const safeText = escapeHTML(unsafeText); // Now safeText can be rendered without executing the script """ ### 7.2 Standard: Avoid eval() **Do This:** * Never use "eval()" or similar functions that execute arbitrary code from strings. **Why:** "eval()" can introduce dangerous security vulnerabilities by executing untrusted code. ### 7.3 Standard: Dependency Management **Do This:** * Keep all dependencies up-to-date to avoid known vulnerabilities. * Regularly audit dependencies for security issues using tools like "npm audit" or "yarn audit". ## 8. Error Handling ### 8.1 Standard: Centralized Error Handling **Do This:** * Implement Error Boundaries to catch JavaScript errors anywhere in the UI and log the errors. * Display a graceful fallback UI instead of crashing the component. **Don't Do This:** * Let errors propagate up to crash the entire application. **Why:** Error Boundaries improve the user experience by preventing the entire application from crashing due to errors in one part. **Example:** """javascript // Error Boundary Component class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service console.error(error, errorInfo); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } } // Wrapping a component <ErrorBoundary> <MyComponent /> </ErrorBoundary> """ These standards cover best practices for component design in JavaScript, promoting reusability, maintainability, performance, and security. By adhering to these guidelines, developers can create high-quality, robust applications.
# State Management Standards for JavaScript This document outlines the coding standards for state management in JavaScript applications. These standards aim to ensure maintainability, readability, performance, and scalability of our JavaScript codebases. They reflect modern best practices, utilizing the latest ECMAScript features and commonly adopted patterns within the JavaScript ecosystem. ## 1. Introduction to State Management in JavaScript State management refers to how application data is handled, stored, and updated. Effective state management is crucial for building complex, interactive JavaScript applications. It ensures that UI components render correctly based on the current application state and that changes to the state are handled predictably and efficiently. **Why is this important?** * **Maintainability:** Centralized state management makes it easier to understand where the application's data lives and how it's modified. * **Performance:** Efficient state updates prevent unnecessary re-renders and computations. * **Scalability:** Well-structured state allows the application to grow without becoming unmanageable. * **Predictability:** Clear data flow leads to fewer bugs and easier debugging. ## 2. Core Principles ### 2.1 Immutability * **Standard:** Treat state as immutable. Avoid modifying state directly. Instead, create new copies with the required changes. * **Why:** Immutability simplifies change detection, enables time-travel debugging, and prevents unexpected side effects. * **Do This:** """javascript // Using the spread operator for immutability const originalState = { name: 'Alice', age: 30 }; const newState = { ...originalState, age: 31 }; // Creates a new object with a modified age console.log(originalState.age); // 30 console.log(newState.age); // 31 // Creating a new array with modifications const originalArray = [1, 2, 3]; const newArray = [...originalArray, 4]; // Creates a new array with 4 appended console.log(originalArray); // [1, 2, 3] console.log(newArray); // [1, 2, 3, 4] """ """javascript // Using Array methods that return new arrays (non-mutating) const originalArray = [1, 2, 3]; const newArray = originalArray.map(x => x * 2); // [2, 4, 6] const filteredArray = originalArray.filter(x => x > 1); // [2, 3] """ * **Don't Do This:** """javascript // Direct state modification (anti-pattern) const state = { name: 'Alice', age: 30 }; state.age = 31; // Modifies the original state object console.log(state.age); // 31 - changes the original object """ ### 2.2 Single Source of Truth * **Standard:** Maintain a single source of truth for your application state. Avoid duplicating state across different components. * **Why:** Reduces inconsistencies, simplifies updates, and improves data integrity. * **Do This:** Use a centralized state management library/pattern like Redux, Zustand, or Context API with reducers to have a single source of truth. * **Don't Do This:** Store the same data in multiple independent component states. ### 2.3 Predictable State Updates * **Standard:** Ensure state updates are predictable. Use pure functions (reducers) to handle state transitions based on specific actions or events. * **Why:** Makes it easier to reason about how the application state changes, allowing for easier debugging and testing. * **Do This:** """javascript // Example reducer function const reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 }; case 'DECREMENT': return { ...state, count: state.count - 1 }; default: return state; } }; //Initial State const initialState = { count: 0 }; // Using the reducer with useReducer hook import React, { useReducer } from 'react'; function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button> <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button> </div> ); } """ * **Don't Do This:** Mutate state directly within event handlers or components, causing unpredictable side effects. ### 2.4 Explicit Data Flow * **Standard:** Establish a clear and explicit data flow. Data should flow in a single direction, from the source of truth to the components that need it. * **Why:** Simplifies debugging, improves performance, and enables better code organization. * **Do This:** Use patterns such as unidirectional data flow (e.g., Redux pattern: Action -> Reducer -> Store -> View) * **Don't Do This:** Allow components to directly modify the state of other components or introduce cyclical dependencies. ## 3. State Management Approaches ### 3.1 Local Component State * **Standard:** Use local component state (using the "useState" hook in React, or similar mechanisms in other frameworks) for data that is only relevant to that component. * **Why:** Keeps components self-contained and reduces unnecessary complexity. * **Do This:** """javascript import React, { useState } from 'react'; function MyComponent() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } """ * **Don't Do This:** Overuse local state for data that is needed by multiple components or represents a global application concern. Move that state higher in the component tree, or into a global state management solution. ### 3.2 Context API * **Standard:** Use the Context API for passing data down the component tree without prop drilling (passing props through intermediate components that don't need them). * **Why:** Avoids prop drilling and makes it easier to share data between distant components. * **Do This:** """javascript // Creating a context import React, { createContext, useState, useContext } from 'react'; const MyContext = createContext(); // Creating a provider function MyProvider({ children }) { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light')); }; const value = { theme, toggleTheme }; return ( <MyContext.Provider value={value}> {children} </MyContext.Provider> ); } // Using the context in a component function MyComponent() { const { theme, toggleTheme } = useContext(MyContext); return ( <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}> <p>Current Theme: {theme}</p> <button onClick={toggleTheme}>Toggle Theme</button> </div> ); } // Wrapping the application with the provider function App() { return ( <MyProvider> <MyComponent /> </MyProvider> ); } """ * **Don't Do This:** Overuse Context API for complex state management needs. For complex scenarios involving frequent updates and intricate logic, consider a dedicated state management library. ### 3.3 Redux * **Standard:** Use Redux for complex applications that require predictable state management, time-travel debugging, and centralized data flow. * **Why:** Facilitates predictable state changes through reducers, enables middleware for side effects, provides debugging tools for complex applications. * **Do This:** """javascript // Actions const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; const increment = () => ({ type: INCREMENT }); const decrement = () => ({ type: DECREMENT }); // Reducer const initialState = { count: 0 }; const reducer = (state = initialState, action) => { switch (action.type) { case INCREMENT: return { ...state, count: state.count + 1 }; case DECREMENT: return { ...state, count: state.count - 1 }; default: return state; } }; // Store import { createStore } from 'redux'; const store = createStore(reducer); // React component import React from 'react'; import { connect } from 'react-redux'; function Counter({ count, increment, decrement }) { return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); } const mapStateToProps = (state) => ({ count: state.count, }); const mapDispatchToProps = { increment, decrement, }; export default connect(mapStateToProps, mapDispatchToProps)(Counter); """ * **Don't Do This:** Apply Redux to simple applications where local component state or the Context API would suffice. Avoid mutating state directly within reducers. ### 3.4 Zustand * **Standard:** Consider Zustand for simpler, yet powerful state management with minimal boilerplate. It's good alternative to Redux with fewer concepts to learn. * **Why:** Zustand offers a more streamlined approach to global state management compared to Redux, reducing boilerplate and complexity. * **Do This:** """javascript import { create } from 'zustand' const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) function Counter() { const { count, increment, decrement } = useStore() return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ) } export default Counter; """ * **Don't Do This:** Disregard Zustand for applications requiring global state due to its simplicity. It is a very capable library that can handle complex scenarios. ### 3.5 Jotai * **Standard:** Consider Jotai if you need more fine-grained control over state dependencies with an atomic approach. * **Why:** Jotai uses an "atoms" based approach where each piece of state is an atom, leading to optimized re-renders and better performance for complex UIs. * **Do This:** """javascript import { atom, useAtom } from 'jotai' const countAtom = atom(0) function Counter() { const [count, setCount] = useAtom(countAtom) return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={() => setCount(count - 1)}>Decrement</button> </div> ) } export default Counter; """ * **Don't Do This:** Dismiss Jotai for its atomic approach. It is well-suited for scenarios needing precise re-render control. ### 3.6 Recoil * **Standard:** Use Recoil for managing a shared state that flows directly from atoms (units of state) through selectors (pure functions that derive state). * **Why:** Designed for React, Recoil allows code-splitting, and avoids unnecessary re-renders by only updating components that subscribe to changes in the affected atoms. * **Do This:** """javascript import React from 'react'; import { RecoilRoot, atom, selector, useRecoilState, } from 'recoil'; // Define an atom (a piece of state) const countState = atom({ key: 'countState', // unique ID (very important for Recoil) default: 0, // initial value }); // Define a selector (derived state) const evenOrOddState = selector({ key: 'evenOrOddState', get: ({ get }) => { const count = get(countState); return count % 2 === 0 ? 'Even' : 'Odd'; }, }); function Counter() { const [count, setCount] = useRecoilState(countState); const evenOrOdd = useRecoilValue(evenOrOddState); return ( <div> <p>Count: {count} - {evenOrOdd}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } function App() { return ( <RecoilRoot> <Counter /> </RecoilRoot> ); } export default App; """ * **Don't Do This:** Use Recoil without understanding its core principles of atoms and selectors. This can lead to inefficient state updates and performance issues. Also, Recoil is React specific, so avoid using it in non-React projects. ## 4. Asynchronous State Management ### 4.1 Handling Side Effects * **Standard:** Manage side effects (e.g., API calls, timers) separately from state updates. Use middleware (e.g., Redux Thunk, Redux Saga) or asynchronous actions to handle side effects. * **Why:** Keeps reducers pure and improves testability. * **Do This (Redux Thunk):** """javascript // Action creator const fetchDataRequest = () => ({ type: 'FETCH_DATA_REQUEST' }); const fetchDataSuccess = (data) => ({ type: 'FETCH_DATA_SUCCESS', payload: data }); const fetchDataFailure = (error) => ({ type: 'FETCH_DATA_FAILURE', payload: error }); // Thunk action to fetch data asynchronously export const fetchData = () => { return async (dispatch) => { dispatch(fetchDataRequest()); try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); dispatch(fetchDataSuccess(data)); } catch (error) { dispatch(fetchDataFailure(error.message)); } }; }; """ * **Do This (Redux Saga):** """javascript // Sagas import { takeLatest, call, put } from 'redux-saga/effects'; // Asynchronous function to fetch data const fetchDataFromApi = async () => { const response = await fetch('https://api.example.com/data'); return await response.json(); }; // Worker Saga: handles the actual API call and dispatches success/failure actions function* fetchDataSaga() { try { const data = yield call(fetchDataFromApi); // call the async function yield put({ type: 'FETCH_DATA_SUCCESS', payload: data }); // dispatch success } catch (error) { yield put({ type: 'FETCH_DATA_FAILURE', payload: error.message }); // dispatch failure } } // Watcher Saga: watches for actions dispatched to the store and calls worker saga export function* watchFetchData() { yield takeLatest('FETCH_DATA_REQUEST', fetchDataSaga); } """ """ javascript // In your Redux setup (e.g., configureStore.js): import createSagaMiddleware from 'redux-saga'; import { configureStore } from '@reduxjs/toolkit'; import rootReducer from './reducers'; // your combined reducers import rootSaga from './sagas'; // your combined sagas // Create the saga middleware const sagaMiddleware = createSagaMiddleware(); // Configure the store const store = configureStore({ reducer: rootReducer, middleware: [sagaMiddleware], // Add the saga middleware }); // Run the root saga sagaMiddleware.run(rootSaga); export default store; """ * **Don't Do This:** Perform API calls or other asynchronous operations directly within reducer functions. ### 4.2 Optimistic Updates * **Standard:** For user-initiated actions (e.g., liking a post), consider implementing optimistic updates to improve the user experience. Optimistically update the UI before the server confirms the change. * **Why:** Makes the application feel more responsive. * **Do This:** """javascript // Optimistically update the state const likePostOptimistic = (postId) => ({ type: 'LIKE_POST_OPTIMISTIC', payload: postId, }); // After the server confirms the like const likePostSuccess = (postId) => ({ type: 'LIKE_POST_SUCCESS', payload: postId, }); // If the server returns an error const likePostFailure = (postId, error) => ({ type: 'LIKE_POST_FAILURE', payload: { postId, error }, }); """ * **Don't Do This:** Implement optimistic updates without handling potential errors or edge cases. Revert the optimistic update if the server request fails. ## 5. Data Fetching and Caching ### 5.1 Libraries * **Standard:** Use a data fetching library like "axios" or the native "fetch" API for making HTTP requests. Consider using a caching library like "react-query" or "swr" for managing fetched data and improving performance. * **Why:** Simplifies data fetching logic, provides built-in caching mechanisms, and improves application performance. * **Do This (react-query):** """javascript import { useQuery } from 'react-query'; const fetchPosts = async () => { const response = await fetch('/api/posts'); return response.json(); }; function Posts() { const { data, isLoading, error } = useQuery('posts', fetchPosts); if (isLoading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return ( <ul> {data.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } """ * **Don't Do This:** Manually implement caching logic or rely on outdated data. ### 5.2 Data Normalization * **Standard:** Normalize fetched data to make it easier to manage and update. Store data in a structured format (e.g., using IDs as keys in an object). * **Why:** Reduces data duplication, simplifies updates, and improves performance. * **Do This:** """javascript // Example data structure { posts: { 1: { id: 1, title: 'Post 1', authorId: 101 }, 2: { id: 2, title: 'Post 2', authorId: 102 }, }, authors: { 101: { id: 101, name: 'Alice' }, 102: { id: 102, name: 'Bob' }, }, } """ * **Don't Do This:** Store denormalized data with redundant information. ## 6. Testing ### 6.1 Unit Tests * **Standard:** Write unit tests for reducers, selectors, and actions to ensure predictable state management behavior. * **Why:** Validates state transitions and prevents regressions. * **Do This:** Use testing frameworks like Jest and testing libraries like React Testing Library. ### 6.2 Integration Tests * **Standard:** Write integration tests for components that interact with the state to verify the data flow and UI updates. * **Why:** Ensures components render correctly based on state changes. ## 7. Naming Conventions * **Standard:** Use consistent and descriptive names for actions, reducers, and state variables. * Actions: "ACTION_TYPE" (e.g., "FETCH_PRODUCTS_REQUEST", "FETCH_PRODUCTS_SUCCESS") * Reducers: "reducerName" (e.g., "productReducer", "userReducer") * State variables: "stateVariableName" (e.g., "products", "userDetails") * **Why:** Improves code readability and maintainability. ## 8. Performance Optimization * **Standard:** Use memoization techniques (e.g., "React.memo", "useMemo", "useCallback") to prevent unnecessary re-renders. * **Why:** Improves application performance by reducing the number of re-renders. """javascript import React, { useState, useCallback, useMemo } from 'react'; // A simple component that displays a name const NameComponent = React.memo(({ name }) => { console.log("NameComponent rendering for ${name}"); return <p>Name: {name}</p>; }); function App() { const [firstName, setFirstName] = useState('John'); const [lastName, setLastName] = useState('Doe'); // useCallback to memoize the handler const updateFirstName = useCallback(() => { setFirstName('Jane'); }, []); // useMemo to memoize the full name const fullName = useMemo(() => { console.log('Calculating fullName'); return "${firstName} ${lastName}"; }, [firstName, lastName]); return ( <div> <NameComponent name={fullName} /> <button onClick={updateFirstName}>Change First Name</button> </div> ); } export default App; """ * **Standard:** Select only the necessary data from the state in components to avoid unnecessary re-renders when other parts of the state change. * **Why:** Reduces the number of components that need to re-render when the state changes. ## 9. Conclusion Adhering to these state management standards will enable us to build robust, maintainable, and performant JavaScript applications. These standards promote predictable state updates, clear data flow, and efficient use of JavaScript's modern features. It's important to remember that state management is not one-size-fits-all; choose the approach that best suits the complexity and scale of your application. Continuous learning and adapting to the evolving JavaScript landscape is an ongoing process, and these guidelines should be revisited and refined as needed to reflect the latest best practices.