# Componentes

Los componentes son piezas de software autónomas diseñadas como cajas negras que reciben otros componentes (o ninguno) para realizar sus tareas. El objetivo principal es desacoplar secciones de código y maximizar la capacidad de prueba.

## Estructura del Directorio

Todos los componentes DEBEN definirse dentro de un directorio dedicado que contenga al menos estos cuatro archivos:

* `component.ts` - Contiene el código de implementación del componente
* `types.ts` - Contiene la interfaz del componente y los tipos expuestos
* `errors.ts` - Contiene todas las clases de error exportadas
* `index.ts` - Exporta la API pública del componente

### Ejemplo de Estructura

```
src/
└── components/
    └── my-component/
        ├── component.ts
        ├── types.ts
        ├── errors.ts
        └── index.ts
```

## Archivo de Tipos

Los componentes se ajustan a una interfaz que define su API pública. Esta interfaz puede compartirse entre componentes intercambiables o utilizarse para entender los métodos que expone el componente.

Todos los componentes que tengan su propia interfaz DEBEN definirla en un `types.ts` archivo dentro del directorio del componente.

Otros tipos expuestos por el componente TAMBIÉN DEBEN colocarse en este archivo.

### Ejemplo: `types.ts`

```tsx
export type Something = {
  aValue: boolean
  anotherValue: string
}

export interface IMyComponent {
  myFunction: () => boolean
  getSomething: () => Something
}
```

### Tipos Compartidos

Los tipos compartidos entre múltiples componentes, como interfaces comunes, DEBEN colocarse en un `types.ts` archivo en el directorio raíz del código fuente del proyecto (`/src/types.ts`).

## Archivo del Componente

El archivo del componente contiene la función creadora del componente. Esta función sigue una convención específica de nombre y estructura.

### Convención de Nomenclatura

Las funciones creadoras de componentes DEBEN llamarse como: `create` + `ComponentName` + `Component`

**Ejemplo:** `createMyNewComponent`

### Firma de la Función

La función creadora DEBE:

1. Recibir un objeto components como primer parámetro que contenga todas las dependencias
2. Opcionalmente recibir parámetros adicionales de configuración
3. Devolver un objeto que contenga los métodos expuestos (públicos)

### Estructura del componente

Al inicio de la función creadora del componente:

1. Extraer dependencias del objeto components
2. Inicializar variables comunes (p. ej., loggers, configuración)
3. Definir funciones auxiliares internas
4. Definir métodos públicos
5. Devolver la API pública

### Ejemplo: `component.ts`

```tsx
import { IMyComponent, Something } from './types'
import { WrongStringError } from './errors'

export function createMyNewComponent(
  components: Pick<AppComponents, 'logs'>
): IMyComponent {
  const { logs } = components
  const logger = logs.getLogger('my-component')

  // Método interno - no expuesto
  function computeString(fstString: string, sndString: string): string {
    if (fstString.length === 0) {
      throw new WrongStringError()
    }
    
    return fstString + ' ' + sndString
  }

  // Método público - expuesto en el objeto de retorno
  function myFunction(): boolean {
    return true
  }

  // Método público - expuesto en el objeto de retorno
  function getSomething(): Something {
    logger.info('Getting something')
    
    return {
      aValue: true,
      anotherValue: computeString('aString', 'anotherString')
    }
  }

  // Devolver la API pública
  return {
    myFunction,
    getSomething
  }
}
```

## Archivo de Errores

El archivo de errores contiene clases de error personalizadas que un componente puede lanzar. Estos errores pueden usarse en otros componentes o controladores para identificar y manejar correctamente condiciones de error específicas.

### Ejemplo: `errors.ts`

```tsx
export class WrongStringError extends Error {
  constructor(message?: string) {
    super(message || 'Incorrect String')
    this.name = 'WrongStringError'
  }
}

export class ComponentNotInitializedError extends Error {
  constructor() {
    super('Component has not been initialized')
    this.name = 'ComponentNotInitializedError'
  }
}
```

### Buenas Prácticas de Manejo de Errores

* Crear clases de error específicas para diferentes condiciones de error
* Incluir mensajes de error significativos
* Establecer el `name` propiedad para que coincida con el nombre de la clase y facilitar la depuración
* Documentar qué errores pueden lanzarse por cada método público

## Archivo Índice

El archivo index sirve como el punto de entrada público para el componente, exportando solo lo que debe ser accesible para otras partes de la aplicación.

### Ejemplo: `index.ts`

```tsx
export { createMyNewComponent } from './component'
export type { IMyComponent, Something } from './types'
export { WrongStringError } from './errors'
```

## Buenas prácticas

1. **Responsabilidad Única** - Cada componente debe tener un propósito claro
2. **Dependencias Explícitas** - Todas las dependencias deben inyectarse a través del parámetro components
3. **Inmutabilidad** - Preferir estructuras de datos inmutables cuando sea posible
4. **Manejo de errores** - Usar clases de error personalizadas para diferentes condiciones de error
5. **Registro (Logging)** - Usar el logger para depuración y monitorización
6. **Seguridad de tipos** - Aprovechar plenamente el sistema de tipos de TypeScript
7. **Documentación** - Documentar métodos complejos y la lógica de negocio

## Ciclo de Vida del Componente

Algunos componentes pueden necesitar realizar operaciones de configuración o limpieza. WKC proporciona métodos de ciclo de vida:

* `[START_COMPONENT]` - Llamado cuando el componente empieza
* `[STOP_COMPONENT]` - Llamado cuando el componente se detiene

Vea la [Adaptadores](https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/componentes-bien-conocidos/adapters) sección para ejemplos de componentes que usan métodos de ciclo de vida.
