{"id":25510285,"url":"https://github.com/gabriel117343/result-pattern-en-javascriptreact","last_synced_at":"2026-01-26T03:32:39.040Z","repository":{"id":275391199,"uuid":"925948563","full_name":"Gabriel117343/Result-Pattern-en-JavaScriptReact","owner":"Gabriel117343","description":"Patrón de Diseño Result Pattern  adaptado a JavaScript/React","archived":false,"fork":false,"pushed_at":"2025-02-04T16:36:10.000Z","size":110,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-27T11:19:12.450Z","etag":null,"topics":["result-pattern"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gabriel117343.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-02T06:25:14.000Z","updated_at":"2025-02-06T15:51:51.000Z","dependencies_parsed_at":"2025-05-20T21:02:39.407Z","dependency_job_id":null,"html_url":"https://github.com/Gabriel117343/Result-Pattern-en-JavaScriptReact","commit_stats":null,"previous_names":["gabriel117343/result-pattern-en-javascriptreact"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Gabriel117343/Result-Pattern-en-JavaScriptReact","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gabriel117343%2FResult-Pattern-en-JavaScriptReact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gabriel117343%2FResult-Pattern-en-JavaScriptReact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gabriel117343%2FResult-Pattern-en-JavaScriptReact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gabriel117343%2FResult-Pattern-en-JavaScriptReact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gabriel117343","download_url":"https://codeload.github.com/Gabriel117343/Result-Pattern-en-JavaScriptReact/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gabriel117343%2FResult-Pattern-en-JavaScriptReact/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28765914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T03:19:35.311Z","status":"ssl_error","status_checked_at":"2026-01-26T03:19:13.815Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["result-pattern"],"created_at":"2025-02-19T09:38:54.745Z","updated_at":"2026-01-26T03:32:39.026Z","avatar_url":"https://github.com/Gabriel117343.png","language":null,"readme":"\n# 📌 Documentación: Result Pattern en JavaScript/React \n\n## Índice\n\n1. **Introducción al Result Pattern**\n   - 1.1. ¿Qué es el Result Pattern?\n   - 1.2. Definición, orígenes e inspiración (Rust, Go, C#)\n   - 1.3. Objetivo principal: manejo explícito de errores sin throw/try-catch\n   - 1.4. Comparación con enfoques tradicionales (ventajas y desventajas)\n   - 1.5. Relevancia en JavaScript/React:\n     - Operaciones asíncronas (`async/await`)\n     - Centralización del manejo de estados complejos (ej.: `useReducer`)\n     - Mejora en legibilidad y mantenibilidad del código\n\n2. **Estructura del Result Pattern en el Proyecto**\n   - 2.1. Objeto de Resultado Estándar\n   - 2.2. Ejemplos de Uso (casos de éxito y error)\n   - 2.3. Flujo de Operaciones (diagrama Mermaid)\n   - 2.4. Campos personalizados en el Resultado\n\n3. **Implementación en Capas Clave**\n   - 3.1. Capa de Manejo de Errores Centralizado (`handleApiError`) y el nuevo **Standardized Error Pattern**\n   - 3.2. Capa de Contexto (`useReducer`)\n   - 3.3. Ejemplos de retorno de datos específicos en funciones asíncronas\n\n4. **Consumo en Componentes React**\n   - 4.1. Ejemplo: Redirección sin esperar al estado global\n   - 4.2. Gestión de estado local en componentes\n\n5. **Ventajas del Enfoque**\n   - 5.1. Claridad y legibilidad en el flujo de control\n   - 5.2. Flexibilidad en el consumo de APIs\n   - 5.3. Reducción de complejidad en el estado global\n\n6. **Consideraciones y Buenas Prácticas**\n   - 6.1. Estructura de objetos y consistencia\n   - 6.2. Seguridad y manejo de datos sensibles\n   - 6.3. Testing y simulación de resultados\n   - 6.4. Diseño de respuestas de la API y contratos claros\n   - 6.5. Documentación de campos personalizados\n7. Casos de Uso Avanzados\n   - 7.1 Manejo de Errores en Flujos Asíncronos Concurrentes\n   - 7.2 Uso del Patrón en Microservicios\n   - 7.3 Manejo de Errores en Operaciones Transaccionales\n8. **Conclusión**\n   - 7.1. Resumen de ventajas\n   - 7.2. Adaptabilidad y escalabilidad del patrón\n   \n---\n\n## 1. Introducción al Result Pattern\n\n### 1. 1 ¿Qué es el Result Pattern?\nUn patrón de diseño inspirado en Rust, Go y C# que encapsula el resultado de operaciones en un objeto estructurado con:\n- **Éxito explícito**: `success: boolean` || `isOk: boolean` \n- **Mensajes descriptivos**: `message: string`\n- **Datos relevantes**: Campos personalizados (ej: `idUser`, `statusCode`)\n\n### ¿Por qué usarlo en React?\n- **Elimina `try/catch` anidados**: Flujo lineal y legible\n- **Centraliza el manejo de errores**: Lógica consistente en toda la app\n- **Facilita el estado global**: Integración natural con `useReducer`\n\n### 1.2. Definición, orígenes e inspiración\nInspirado en lenguajes como **Rust**, **Go** y **C#**, el *Result Pattern* se utiliza para evitar el uso excesivo de `throw` y `try-catch`, promoviendo un manejo explícito de los resultados.\n\n\u003e [!NOTE]\n\u003eNota: Aunque el patrón proviene de otros lenguajes, en JavaScript se adapta para trabajar con promesas y async/await.\n\n### 1.3. Objetivo principal\nProveer un mecanismo para retornar el resultado de una operación (**éxito o error**) en un objeto estándar, permitiendo que el consumidor de la función decida cómo manejar cada caso, sin depender de excepciones.\n\n### 1.4. Comparación con enfoques tradicionales\n\n#### 🔴 **Enfoque tradicional (try-catch)**\n```javascript\n// UserContex.jsx (función reutilizable dentro de un estado global)\nasync function fetchUserData(userId) {\n  try {\n    const response = await fetch(`/api/users/${userId}`);\n    if (!response.ok) {\n      throw new Error(\"Error al obtener el usuario\");\n    }\n    try {\n      const proccessData = proccess(data)\n      return proccessData\n    } catch (e) {\n      console.error(\"Error al procesar la información\", e)\n    }\n    // return await response.json();\n  } catch (error) {\n    console.error(\"Error de red\", error);\n    return null; // ❌ No hay una forma clara de manejar este error\n  }\n}\n```\n🔹 **Desventajas:**\n- Los errores son capturados dentro de `catch`, lo que puede hacer que los errores pasen desapercibidos.\n- La función devuelve `null`, lo cual no indica explícitamente el motivo del error.\n- No hay un contrato claro de respuesta.\n- Posible necesidad de anidar try-catch debido a manejo de diferentes tipos de procesos\n\n\u003cdetails\u003e \u003csummary\u003eVer Ejemplo función proccessData✅\u003c/summary\u003e\n   \n```javascript\n// Simulamos una función que llama a una API externa para procesar datos.\nasync function processDataApi(data) {\n  // Simula una llamada a la API y su respuesta\n  // Por ejemplo, se podría utilizar axios, pero aquí usamos fetch para simplificar.\n  const response = await fetch('https://api.example.com/process', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(data)\n  });\n  return response; // Se asume que response tiene .status y .json()\n}\n\nasync function processData(data) {\n  try {\n    // Llamamos a la API de procesamiento\n    const response = await processDataApi(data);\n    \n    // Procesamos la respuesta de la API\n    if (response.status === 200) {\n      const processedData = await response.json();\n      return {\n        success: true,\n        message: \"Datos procesados correctamente\",\n        data: processedData\n      };\n    } else {\n      // Si el estado no es 200, consideramos que hubo un error en la API\n      return {\n        success: false,\n        message: `Error en el procesamiento: estado ${response.status}`,\n        status: response.status\n      };\n    }\n  } catch (error) {\n    // Capturamos errores de red o excepciones internas\n    return {\n      success: false,\n      message: error.message || \"Error inesperado en el procesamiento de datos\",\n      status: error.status || 500\n    };\n  }\n}\n\n```\n\u003c/details\u003e\n\n#### 🟢 **Enfoque con Result Pattern** (Async Function)\n```javascript\n// UserContext.jsx (función reutilizable dentro de un estado global)\n\nimport { getUserApi } from '@api/users.api' // api externa con axios\nexport const UsersContext = createContext(null)\n\nexport const UsersProvier = ({ children }) =\u003e {\n  const initialState = { users: [], count: 0 }\n  const [stateUser, dispatch] = useReducer(UsersReducer, initialState)\n  \n  // getUsersContext, createUserContext...\n\n  export const getUserContext = async (userId) =\u003e {\n\n   try {\n      const res = await getUserApi(userId)\n\n       // o primero podría proccesar la info \u003e Llamamos a la función processData para procesar los datos sin necesidad de anidar aquí un try-catch adicional.\n       // const processedResult = processData(res.data);\n       // if (!processedResult.success) {\n         // Si el procesamiento falla, se retorna el error.\n         // Opcionalmente, se puede utilizar handleApiError para transformar el error, si processedResult.error existe.\n         // return handleApiError(processedResult.error) || processedResult;\n       // }\n        // Si todo es exitoso, se retorna el resultado final con los datos procesados.\n       // return { \n         // success: true, \n         // message: 'Usuario procesado correctamente', \n         // data: processedResult.data \n        // };\n      if (res.status === 200) {\n        dispatch({\n          type: 'GET_USER',\n          payload: res.data\n        })\n        return { success: true, message: res.data.message }\n      }\n      \n       return { success: false, message: res.data.message ?? 'Error inesperado' }\n    }  catch (error) {\n      return { success: false, message: res.data.error ?? 'Error inesperado' } \n      // return handleApiError(error) \u003e ya que puede ser .error | .detail | etc.. o información de error extensa\n      // en lugar de empezar a introducir múltiples throw new Error aquí\n    }\n  \n  }\n  // retorno de la función \n}\n// otro componente utilizando la función del contexto\n\nconst getUserById = async (id) =\u003e {\n  \n  const { success, message, userId } = await getUserContext(id)\n  if (success) {\n    // mostrar el mensaje con toast \n  } else {\n    // toast.error(message)\n  }\n}\n\n```\n\n\u003e [!NOTE]\n\u003e Las funciones async siempre devuelven una promesa, incluso si no se especifica explícitamente. \n\u003e Retornar un valor dentro de un try o catch equivale a resolver la promesa (Promise.resolve()).\n\u003e Lanzar un error (throw) dentro de un catch equivale a rechazar la promesa (Promise.reject()).\n\n✅ **Ventajas:**\n- Se evita el uso de `try-catch` innecesario dentro del llamada de la función compartid por el contexto Global.\n- El objeto de resultado es explícito (`success: true/false`).\n- Se pueden agregar mensajes y códigos de estado personalizados.\n- Se puede extender a una utilidad/función encargada de recopilar datos de error útiles (ej: return handleApiError(error) )\n\n### 1.5. Relevancia en JavaScript/React\n\n- 🔹 **Asincronía:** Se integra de forma natural con `async/await`.\n- 🔹 **Gestión de estados:** Es ideal para contextos que usan `useReducer` en React, centralizando el manejo de datos y errores.\n- 🔹 **Legibilidad:** Mejora la claridad del código al separar la lógica de manejo de errores de la lógica de negocio.\n---\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003ePatrón de Diseño: Promise con Resolve/React\u003c/strong\u003e\u003c/summary\u003e\n\nEl patrón de diseño basado en Promesas con `resolve`/`reject`es un enfoque tradicional en JavaScript para manejar operaciones asíncronas. Este patrón utiliza el objeto **Promise** para representar un valor que puede estar disponible ahora, en el futuro o nunca. Las funciones que implementan este patrón resuelven (`resolve`) cuando la operación tiene éxito o rechazan (`reject`) cuando ocurre un error.\n\n### Ventajas\n- 1 **Familiaridad**: Es ampliamente utilizado en JavaScript, por lo que muchos desarrolladores están familiarizados con él.\n- 2 **Separación clara entre éxito y error**: **resolve** indica éxito, mientras que **reject** indica un error. Esto facilita el manejo diferenciado de ambos casos.\n- 3 **Compatibilidad con `async`/`await`**: Las promesas son compatibles con async/await, lo que simplifica su uso en funciones asíncronas.\n---\n### Desventajas\n- 1 **Dependencia de Excepciones**: Requiere el uso de `try-catch` o .then / .catch() (cuando se consumen) para manejar errores, lo que puede llevar a código más complejo y menos predecible.\n- 2 **Falta de consistencia**: No hay un contrato estándar para los resultados. Cada función puede devolver diferentes estructuras, lo que dificulta la integración.\n- 3 **Anidamiento innecesario**: En aplicaciones grandes, especialmente en React, el uso de multiple de try-catch puede llevar a múltiples niveles de anidamiento.\n\n### Ejemplo de código\n   \n```javascript \nfunction buscarEnBaseDeDatos(id) {\n  return new Promise((resolve, reject) =\u003e {\n    try {\n      const resultado = BaseDatos.buscar(id);\n      if (resultado !== null) {\n        resolve(resultado); // resolver Éxito\n      } else {\n        resolve(0); // Indica que no se encontró el registro\n      }\n    } catch (error) {\n      reject(\"Error al acceder a la base de datos\"); // Error\n    }\n  });\n}\n// Consumo con Promesas\nbuscarEnBaseDeDatos(123)\n  .then((resultado) =\u003e {\n    if (resultado === 0) {\n      console.log(\"No se encontró el registro\");\n    } else {\n      console.log(\"Registro encontrado:\", resultado);\n    }\n  })\n  .catch((error) =\u003e {\n    console.error(\"Error:\", error);\n  });\n```\n\u003c/details\u003e\n\n## 2. Estructura del Result Pattern en el Proyecto\n\n### 2.1. Objeto de Resultado Estándar\n\n```javascript\n{\n  success: boolean,   // Indica si la operación fue exitosa o fallida\n  message: string,    // Mensaje descriptivo\n  data?: any,         // Datos adicionales (opcional)\n  status?: number     // Código de estado HTTP (opcional)\n}\n```\n\n### 2.2. Ejemplos de Uso\n\n✅ **Caso de éxito:**\n```javascript\n{ success: true, message: \"Usuario creado\", data: { id: 123 } }\n```\n\n❌ **Caso de error:**\n```javascript\n{ success: false, message: \"Error de red\", status: 500 }\n```\n\n---\n\n## 3. Implementación en Capas Clave\n\n### 3.1. Capa de Manejo de Errores Centralizado (`handleApiError`)  y el nuevo Standardized Error Pattern\n\n\u003cdetails\u003e \u003csummary\u003eVer Diagrama de ejemplo✅\u003c/summary\u003e\n\u003cimage src='https://github.com/user-attachments/assets/8be1b2e5-0369-4e67-b09d-dabf7435c581' /\u003e\n\u003c/details\u003e \n\n- 🔹 **Propósito:**:   \n- Este nuevo patrón se encarga de transformar diversas fuentes de error, ya sea que provengan de la respuesta de una API (por ejemplo, `error.response.data.error`, `error.response.data.message` o `error.response.data.detail`), o de casos en los que el error proviene de `error.request`, o incluso errores generados automáticamente durante la configuración de la solicitud.  \n- Es especialmente útil en etapas tempranas de desarrollo, cuando las APIs aún pueden no devolver errores sólidos o uniformes.  \n-  Este enfoque complementa al Result Pattern agregando el `message` obtenido mediante extractores/limpiadores de errores y siempre incluye un `status` asociado. El retorno siempre seguirá un formato similar a:  \n  `return { success: false, message: extractedMessage, status: statusValue }`.\n\n#### 🔹 **Ventajas del Standardized Error Pattern:**\n- **Consistencia:** Garantiza que todos los errores tengan un formato unificado.\n- **Claridad:** Proporciona mensajes claros extraídos de diversas propiedades (como `error`, `message`, o `detail`).\n- **Robustez durante el desarrollo:** Útil cuando las APIs retornan errores no uniformes, asegurando que siempre se devuelva un `status` junto con el mensaje.\n- **Integración:** Se complementa naturalmente con el Result Pattern al retornar un objeto que incluye `success`, `message` y `status`.\n\n#### 🔹 **Ventajas Clave del Standardized Error Pattern**\n- **Depuración avanzada para desarrolladores**:\n   - Al registrar el error completo en la consola (por ejemplo, usando `console.error`), se proporciona un contexto detallado para la depuración, sin afectar la experiencia del usuario final.\n   - Esto es especialmente útil cuando las **APIs** devuelven errores complejos o anidados, ya que los desarrolladores pueden inspeccionar toda la estructura del error.\n- **Mensajes limpios para los usuarios :**\n   - Mientras tanto, el retorno del error sigue siendo limpio y estructurado (success, message, status), lo que permite mostrar mensajes claros y útiles en la interfaz (como toasts, modales o notificaciones).\n   - Esto garantiza que los usuarios reciban información relevante sin exponer detalles técnicos que puedan confundirlos.\n\n\u003cdetails\u003e \u003csummary\u003eVer ejemplo de funcionamiento ✅\u003c/summary\u003e\n\n```javascript\nfunction handleApiError(error) {\n  // respues de servidor\n  if (error.response) {\n  const { status, data, headers } = error.response\n    // Registro detallado del error para depuración para el desarrollador\n  if (error.response) {\n    console.error(\"❌ ERROR_RESPONSE:\", { \n      status: status,\n      data: data,\n      headers: headers \n    });\n  // Función helper para formatear mensajes de error\n    const formatErrorMessage = (message) =\u003e {\n      return typeof message === 'string'\n        ? message\n        : Array.isArray(message)\n          ? message[0]\n          : JSON.stringify(message)\n    }\n\n    // Función para manejar errores anidados\n    const extractNestedError = (errorData) =\u003e {\n      if (typeof errorData === 'string') return errorData\n      if (Array.isArray(errorData)) return errorData[0]\n      if (typeof errorData === 'object') {\n        const firstValue = Object.values(errorData)[0]\n        return formatErrorMessage(firstValue)\n      }\n      return null\n    }\n\n    // Prioridad en el manejo de mensajes (puede estar en error.response.data.error, data.message o data.detail)\n    const errorPriority = [\n      { key: 'message', transform: (msg) =\u003e msg },\n      { key: 'error', transform: extractNestedError },\n      { key: 'detail', transform: (msg) =\u003e (msg?.length \u003c 100 ? msg : null) },\n      {\n        key: 'non_field_errors',\n        transform: (errors) =\u003e (Array.isArray(errors) ? errors[0] : errors)\n      }\n    ]\n    // otros casos\n    // Buscar el primer mensaje de error válido según la prioridad\n    // Si es un objeto de errores de validación\n    // Mensajes predeterminados según código HTTP\n    const httpErrorMessages = {\n      400: 'Los datos proporcionados no son válidos. Por favor, verifica la información.',\n      401: 'Sesión expirada. Por favor, vuelve a iniciar sesión.',\n      //...\n    }\n    return { success: false, message: httpErrorMessages[status] ||\n        `Error inesperado (${status}). Por favor, intenta nuevamente.`, status }\n    if (error.request)  {\n    // Error de red o solicitud no completada\n      console.error(\"❌ SIN RESPUESTA DEL SERVIDOR:\", error.request);\n    }\n    if (error.config) // Error en la configuración de la solicitud\n  }\n  \n  return {\n    success: false,\n    message: error.message ?? \"Error desconocido\",\n    status: error.status ?? 500\n  };\n}\n```\n\n🔹 **Integración en el Contexto:**\n```javascript\nexport const updateUserContext = async (id, usuario) =\u003e {\n  try {\n    const res = await updateUser(id, usuario);\n    if (res.status === 200) {\n      // Dispatch y lógica de éxito\n      return { success: true, message: res.data.message };\n    }\n  } catch (error) {\n      // uso dentralizado del patrón de error\n     return {\n    ...handleApiError(error), // success: false, message: 'mensaje limpio', status: 400\n    witOutErrorPattern: error.response.data.message ?? 'Error desconocido' // el error puede no estár en el .message❌ \n    }\n  }\n};\n```\n\u003c/details\u003e\n---\n\n### 3.2. Capa de Contexto (`useReducer`)\n\nEn el contexto de usuarios, cada función asíncrona retorna un objeto siguiendo el contrato del Result Pattern. Por ejemplo:\n\n```javascript\nconst createUserContext = async (user) =\u003e {\n  try {\n    const res = await createUser(user);\n    if (res.status === 200 || res.status === 201) {\n      dispatch({ type: 'CREATE_USER', payload: res.data });\n      return { success: true, message: res.data.message, userId: res.data.id }; // opcionalmente podemos incluir más información en este respuesta como el idUser\n    }\n    return { success: false, message: res.data.error ?? 'Error inesperado' };\n  } catch (error) {\n    return handleApiError(error)\n  }\n};\n\n```\n\u003e [!TIP]\n\u003e Esta función combina el patrón de resultados con la integración a React, permitiendo que el componente actúe según el valor retornado sin lanzar excepciones.\n\n---\n### 3.3. Ejemplos de retorno de datos específicos\nPermite retornar información adicional que puede usarse directamente en la UI:\n```javascript\n// Ejemplo en createUserContext\nreturn { \n  ...handleApiError(),\n  userId: res.data.id // Dato específico para redirección inmediata\n};\n```\n---\n## 4 Consumo en Componentes React\n### 4.1 Ejemplo: Redirección sin Estado Global\nUn componente puede usar el resultado devuelto para redirigir o mostrar notificaciones sin esperar a que el estado global se actualice:\n```jsx\nconst handleSubmit = async () =\u003e {\n  const { success, userId } = await createUserContext(formData);\n  if (success) {\n    navigate(`/users/${userId}`); // Redirección directa usando el ID devuelto sin depender del estado global 🚀\n  }\n};\n```\n\u003e [!DANGER]\n\u003e En ocaciones una función puede ejecutarse antes de que siquiera el estado global pueda actualizarse, lo que puede llevar problemas, el obtener el id a través del retorno inmediato asegura el id para la ruta.\n\n### 4.2 Gestión de Estado Local en Componentes\nPermite actualizar estados locales (como cerrar modales o resetear formularios) basándose en el resultado de la operación:\n```jsx\nconst [isModalOpen, setIsModalOpen] = useState(true);\n\nconst handleCreateUser = async () =\u003e {\n  const { success } = await createUserContext(data);\n  if (success) {\n    setIsModalOpen(false); // Cierra el modal sin depender del estado global\n  }\n};\n```\n---\n## 5 Ventajas del enfoque\n### 5.1 Claridad y legibilidad\n- Cada función retorna explícitamente un objeto que indica el éxito o fallo, eliminando la necesidad de múltiples `try-catch`.\n- El flujo de control es más claro y predecible. \n### 5.2 Flexibilidad en el consumo de APIs\n- Se pueden incluir campos personalizados (por ejemplo, `idUser`) que permiten actuar de inmediato en la UI (como redirecciones) sin esperar a la actualización global.\n### 5.3 Reducción de complejidad en el estado global\n- No es necesario reflejar cada cambio de la API en el contexto global si solo un componente requiere la información.\n- Esto reduce la sobrecarga y mejora el rendimiento y la mantenibilidad del sistema.\n---\n## 6 Consideraciones y Buenas Prácticas\n### 6.1 Estructura de Objetos\n- **Consistencia**: Asegúrate de que todas las funciones retornen un objeto con el mismo formato (success, message, data, etc.). Esto garantiza que los consumidores del patrón no tengan que lidiar con respuestas inconsistentes.\n- **Extensibilidad**: Permitir la adición de campos personalizados sin romper el contrato. . Por ejemplo, puedes incluir campos como `userId` o `status` según sea necesario.\n\n\u003e [!WARNING]\n\u003e Mantener una estructura consistente es crucial para evitar errores en la integración entre componentes y servicios.\n\n### 6.2 Seguridad\n- **Tokens y autenticación**: Utilizar interceptores en Axios para la renovación automática de tokens. Esto asegura que las solicitudes siempre estén autenticadas.\n- **Protección de datos**: Evita registrar campos sensibles como contraseñas o información personal en los mensajes de error.\n\n\u003cdetails\u003e \u003csummary\u003eVer Ejemplo de Interceptores con Axios✅\u003c/summary\u003e\n\n```javascript\n// axios.interceptor.js\nimport axios from 'axios';\n\nconst API_URL = Object.freeze({\n  desarrolo: 'http://api-extern',\n  produccion: 'http://api.v1...', // Nota: útilizar una variable de entorno con la url de producción .env para seguridad\n  despliege_local: 'http://ngrok..'\n});\n\n/**\n * Crea una instancia personalizada de Axios con interceptores.\n * @param {string} path - El endpoint base para esta instancia (ej: 'users/', 'login/').\n * @returns {AxiosInstance} - Una instancia de Axios configurada.\n */\nexport const createApiInstance = (path = '') =\u003e {\n  // Crear una instancia de Axios con la URL base y el path específico\n  const apiInstance = axios.create({\n    baseURL: `${API_URL.desarrollo}/${path}`, // Path dinámico para cada API\n  });\n\n  // 🔧 Interceptor de solicitud: Inyecta el token de autenticación\n  apiInstance.interceptors.request.use((config) =\u003e {\n    const token = localStorage.getItem('authToken');\n    if (token) {\n      config.headers.Authorization = `Bearer ${token}`;\n    }\n    return config;\n  });\n\n  // 🔧 Interceptor de respuesta: Maneja errores y renovación de tokens\n  apiInstance.interceptors.response.use(\n    (response) =\u003e response,\n    async (error) =\u003e {\n      if (error.response?.status === 401) {\n        // Renovar token automáticamente\n        const newToken = await refreshToken();\n        localStorage.setItem('authToken', newToken);\n        error.config.headers.Authorization = `Bearer ${newToken}`;\n        return apiInstance(error.config); // Reintentar la solicitud\n      }\n      return Promise.reject(error);\n    }\n  );\n\n  return apiInstance;\n};\n\n// user.api.js\nimport { createApiInstance } from './config/defaultAxiosConfig';\n\n/**\n * Instancia de Axios específica para el módulo de usuarios.\n * Define el endpoint base ('api/users') para todas las solicitudes relacionadas con usuarios.\n */\nconst userApi = createApiInstance('api/users'); // 👈 Endpoint específico para usuarios\n\n/**\n * Obtiene un usuario por su ID.\n * @param {string} userId - ID del usuario a obtener.\n * @returns {Promise\u003cAxiosResponse\u003e} - Respuesta de la API.\n */\nexport const getUser = async (userId) =\u003e {\n  return userApi.get(`/${userId}`); // 👈 Endpoint dinámico basado en el ID\n};\n\n// demás funciones...\n\n/**\n * NOTA:\n * Este archivo tiene la única responsabilidad de definir los endpoints específicos\n * para consumir APIs relacionadas con usuarios (ej: 'api/users').\n * \n * - Los interceptores y la lógica de manejo de tokens están centralizados en `createApiInstance`.\n * - Los endpoints se pasan como parámetros a la configuración de Axios, asegurando que este archivo\n *   sea puramente declarativo y se enfoque en definir las rutas de la API.\n * \n * Para manejar errores y estados globales, se recomienda importar e integrar estas en un archivo de Contexto global Ej: UserContext.jsx con patrones como:\n * - **Result Pattern**: Encapsula resultados (éxito o error) en objetos estándar.\n * - **Standardized Error Pattern**: Centraliza el manejo de errores con mensajes claros y códigos de estado.\n * \n * Esto permite que este archivo sea modular, reutilizable y fácil de mantener.\n */\n```\n\n\u003e [!TIP]\n\u003e 💡Un `endpoint` es un punto de acceso específico en una API al que se envían solicitudes para realizar operaciones (como obtener, crear, actualizar o eliminar datos). Por ejemplo: /api/users es un endpoint para gestionar usuarios.***\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003eVer: Sin este enfoque 🔴\u003c/summary\u003e\n   \n```jsx\n// UserContext.jsx\nimport axios from 'axios';\n\nexport const UsersContext = createContext(null);\n\nexport const UsersProvider = ({ children }) =\u003e {\n  const [users, setUsers] = useState([]);\n  const [error, setError] = useState(null);\n  const [loading, setLoading] = useState(false);\n\n  // 🔴 Problema: Esta función tiene múltiples responsabilidades. 🤯\n  // - Maneja el estado global (users, error, loading).\n  // - Realiza la llamada a la API.\n  // - Procesa los datos.\n  // - Maneja errores directamente aquí.\n  const fetchUserData = async (userId) =\u003e {\n    setLoading(true); // Estado de carga mezclado con lógica de negocio.\n    try {\n      const response = await axios.get(`/api/users/${userId}`);\n      if (!response.data) {\n        throw new Error(\"Datos inválidos\"); // ❌ Lanzamiento de excepciones innecesario.\n      }\n\n      // Procesamiento de datos dentro de la misma función.\n      const processedData = processData(response.data);\n\n      // Actualización del estado global directamente aquí.\n      setUsers((prevUsers) =\u003e [...prevUsers, processedData]);\n      setError(null);\n    } catch (err) {\n      // ❌ Manejo de errores dentro del mismo bloque.\n      console.error(\"Error al obtener usuario:\", err.message);\n      setError(err.message || \"Error desconocido\"); Mensajes de error inconsistentes.\n    } finally {\n      setLoading(false); // Estado de carga mezclado con lógica de negocio.\n    }\n  };\n\n  // Función adicional para procesar datos, pero está acoplada a la lógica principal.\n  const processData = (data) =\u003e {\n    if (!data.name || !data.email) {\n      throw new Error(\"Datos incompletos\"); // ❌ Más lanzamiento de excepciones.\n    }\n    return { id: data.id, name: data.name.toUpperCase(), email: data.email };\n  };\n\n  return (\n    \u003cUsersContext.Provider value={{ users, error, loading, fetchUserData }}\u003e\n      {children}\n    \u003c/UsersContext.Provider\u003e\n  );\n};\n```\n### Problemas identificados :\n- 1 **Múltiples responsabilidades** :\nLa función fetchUserData maneja el estado global (users, error, loading), realiza la llamada a la API, procesa los datos y maneja errores. Esto viola el principio de separación de responsabilidades . 🤯\n- 2 **Manejo de errores inconsistente** :\nLos errores se manejan directamente con try-catch, lo que puede llevar a mensajes de error inconsistentes y falta de claridad sobre qué hacer en cada caso. ❌\n- 3 **Acoplamiento de lógica** :\nLa función processData está acoplada a fetchUserData, lo que dificulta reutilizarla en otros contextos. \n- 4 **Estado global sobrecargado** :\nEl estado global (users, error, loading) se actualiza directamente dentro de la función, lo que puede causar problemas de mantenibilidad y rendimiento si el estado crece. \n- 5 **Falta de contrato claro** :\nNo hay un formato estándar para los resultados (éxito o error), lo que dificulta su consumo en componentes. ❓\n- 6 **Difícil de probar** :\nDebido a la mezcla de lógica de negocio, manejo de errores y estado global, es más complicado escribir pruebas unitarias o de integración para esta función. 🛠️\n\u003cdetails/\u003e\n   \n### 6.3 Testing\n- **Mocking**: Simular respuestas con success: true y success: false para probar la lógica de los componentes.\n- **Pruebas de errores**: Verificar que los mensajes y la lógica de manejo de errores sean correctos.\n#### 6.3.1 Testing con Vitest\n`Vitest` es una herramienta de testing moderna y rápida, compatible con `Vite`. Es ideal para proyectos que utilizan frameworks como **React**, **Vue** o **Svelte**.\nA continuación, se muestran ejemplos de cómo probar funciones que usan el Result Pattern con Vitest.\n\u003cdetails\u003e \u003csummary\u003eVer Ejemplo de Testing con Vitest✅\u003c/summary\u003e\n\n```javascript\n// users.test.ts\nimport { describe, it, expect, vi } from 'vitest';\nimport { getUserContext } from './UserContext';\nimport { getUserApi } from '@api/users.api';\n\nvi.mock('@api/users.api', () =\u003e ({\n  getUserApi: vi.fn(),\n}));\n\ndescribe('getUserContext', () =\u003e {\n  it('should return success result for valid user', async () =\u003e {\n    getUserApi.mockResolvedValue({ status: 200, data: { id: 'validId', name: 'John Doe' } });\n    const result = await getUserContext('validId');\n    expect(result).toEqual({\n      success: true,\n      message: 'Usuario obtenido correctamente',\n      data: { id: 'validId', name: 'John Doe' },\n    });\n  });\n\n  it('should return error result for invalid user', async () =\u003e {\n    getUserApi.mockRejectedValue({ response: { status: 404, data: { message: 'User not found' } } });\n    const result = await getUserContext('invalidId');\n    expect(result).toEqual({\n      success: false,\n      message: 'User not found',\n      status: 404,\n    });\n  });\n});\n```\n\u003c/details\u003e\n\n### 6.3.2 Nuevas Características de Promesas\nEl uso de métodos avanzados de promesas como **Promise.allSettled()** y **Promise.any()** puede mejorar significativamente la robustez de tu código al manejar múltiples operaciones asíncronas.\n\n---\n### 6.5. Documentación de Campos Personalizados 🛠️\n- Mantener un registro de los campos retornados por cada función del contexto para evitar inconsistencias y sobrecarga de datos.\n---\n## 7. Casos de Uso Avanzados\n### 7.1 Manejo de Errores en Flujos Asíncronos Concurrentes\nEn aplicaciones modernas, es común tener flujos asíncronos concurrentes (por ejemplo, cargar datos desde varias APIs al mismo tiempo). El **Result Pattern** puede extenderse para manejar estos casos de manera eficiente.\n\n\u003c/details\u003e\u003csummary\u003eVer Ejemplo: Combinación de Resultados Concurrentes\u003c/summary\u003e\n\n```javascript\n// utils.js\nexport const combineResults = (results) =\u003e {\n  const hasError = results.some((result) =\u003e !result.success);\n  if (hasError) {\n    return {\n      success: false,\n      message: \"Uno o más procesos fallaron\",\n      errors: results.filter((result) =\u003e !result.success),\n    };\n  }\n  return {\n    success: true,\n    message: \"Todos los procesos completados con éxito\",\n    data: results.map((result) =\u003e result.data),\n  };\n};\n\n// App.jsx\nconst fetchData = async () =\u003e {\n  const [userResult, orderResult] = await Promise.all([\n    getUserContext(\"validId\"),\n    getOrderContext(\"orderId\"),\n  ]);\n\n  const combinedResult = combineResults([userResult, orderResult]);\n  if (!combinedResult.success) {\n    console.error(combinedResult.errors);\n  } else {\n    console.log(combinedResult.data);\n  }\n};\n```\nExplicación:\n**Combinación de resultados** : La función combineResults agrupa los resultados de múltiples llamadas y determina si hubo algún error.\n**Manejo centralizado** : Si hay errores, se devuelve un objeto con detalles sobre qué procesos fallaron.\n\n\u003c/details\u003e\n\n### 7.2 Uso del Patrón en Microservicios\nEn arquitecturas de microservicios, el **Result Pattern** puede ser útil para estandarizar las respuestas entre servicios y facilitar el manejo de errores distribuidos.\n\n\u003cdetails/\u003e\u003csummary\u003eVer Ejemplo: Gateway de API\u003c/summary\u003e\n\n```javascript\n// api-gateway.js\nexport const callService = async (serviceUrl, payload) =\u003e {\n  try {\n    const response = await fetch(serviceUrl, {\n      method: \"POST\",\n      body: JSON.stringify(payload),\n    });\n\n    if (!response.ok) {\n      throw new Error(`Service error: ${response.status}`);\n    }\n\n    const data = await response.json();\n    return { success: true, message: \"Success\", data };\n  } catch (error) {\n    return handleApiError(error); // Centraliza el manejo de errores\n  }\n};\n\n// Usage in a gateway\nconst processRequest = async (request) =\u003e {\n  const userServiceResult = await callService(\"http://user-service\", request.user);\n  const orderServiceResult = await callService(\"http://order-service\", request.order);\n\n  const combinedResult = combineResults([userServiceResult, orderServiceResult]);\n  return combinedResult;\n};\n```\n- **Explicación:**\n- **Estandarización** : Cada servicio retorna un objeto siguiendo el **Result Pattern** , lo que facilita la integración.\n- **Centralización** : El manejo de errores se realiza en un solo lugar (`handleApiError`), reduciendo la duplicación de código.\n\u003c/details\u003e\n\n### 7.3 Manejo de Errores en Operaciones Transaccionales\nEn sistemas donde las operaciones deben ser atómicas (todas las operaciones tienen éxito o ninguna), el **Result Pattern** puede combinarse con patrones como **Saga** para revertir cambios en caso de errores.\n\u003cdetails\u003e\u003csummary\u003eVer Ejemplo: Transacciones con Rollback\u003c/summary\u003e\n   \n```javascript\nconst executeTransaction = async (operations) =\u003e {\n  const results = [];\n  try {\n    for (const operation of operations) {\n      const result = await operation();\n      if (!result.success) {\n        throw new Error(\"Transaction failed\");\n      }\n      results.push(result);\n    }\n    return { success: true, message: \"Transaction completed\", data: results };\n  } catch (error) {\n    // Rollback logic\n    for (const result of results) {\n      if (result.rollback) {\n        await result.rollback();\n      }\n    }\n    return { success: false, message: error.message };\n  }\n};\n```\n- **Explicación:**\n- **Atomicidad** : Si alguna operación falla, se revierten todas las operaciones previas.\n- **Flexibilidad** : Cada operación puede incluir lógica de rollback personalizada.\n\u003c/details\u003e\n\n## 8. Conclusión\n\n🔹 **Resumen de ventajas:**\n- 🔄 **Evita `try-catch` innecesarios**.\n- ✅ **Manejo centralizado y explícito de errores**.\n- 📈 **Mejor estructura para estados y promesas**.\n\n🔹 **Adaptabilidad y escalabilidad:**\n\n---\n\n**Referencia Adicional:**  \n\n[Goodbye Exceptions: Mastering Error Handling in JavaScript with the Result Pattern](https://dev.to/gautam_kumar_d3daad738680/goodbye-exceptions-mastering-error-handling-in-javascript-with-the-result-pattern-26kb#:~:text=The%20Result%20Pattern%20is%20a,explicitly%20indicates%20success%20or%20failure.)\n\n[Lemon Code🍋 - JavaScript Asíncronico La Guía definitiva](https://lemoncode.net/lemoncode-blog/2018/1/29/javascript-asincrono)\n\n---\n\n*Documentación generada y mejorada a partir de los archivos de un proyecto Desarrollado y las mejores prácticas actuales (o tendencias ) en el desarrollo en React.*\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabriel117343%2Fresult-pattern-en-javascriptreact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgabriel117343%2Fresult-pattern-en-javascriptreact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabriel117343%2Fresult-pattern-en-javascriptreact/lists"}