Hone logo
Hone
Problems

React Middleware System for Data Fetching and Error Handling

This challenge asks you to build a reusable middleware system for handling data fetching and error management within a React application. Middleware allows you to intercept and modify actions before they reach your components, providing a centralized place for common tasks like data loading indicators, error logging, and data transformation. This is a valuable pattern for maintaining clean and organized React codebases.

Problem Description

You need to create a useMiddleware hook in TypeScript that allows components to subscribe to and utilize middleware functions for data fetching and error handling. The hook should accept an array of middleware functions and a data fetching function as arguments. Each middleware function will receive the data fetching function, the request parameters, and a callback function to resolve or reject the promise returned by the data fetching function.

Key Requirements:

  • useMiddleware Hook: This hook should be the core of your solution. It should accept an array of middleware functions and a data fetching function.
  • Middleware Functions: Each middleware function should be of type (fetchFn: () => Promise<any>, params: any, resolve: (data: any) => void, reject: (error: any) => void) => void. This means they receive the fetch function, request parameters, and resolve/reject callbacks.
  • Sequential Execution: Middleware functions should be executed sequentially, one after the other. The output of one middleware should influence the next.
  • Error Handling: Middleware should be able to intercept and handle errors from the data fetching function.
  • Data Transformation: Middleware should be able to transform the data returned by the data fetching function.
  • Promise Resolution/Rejection: The resolve and reject callbacks provided to the middleware should be used to control the final resolution or rejection of the promise returned by the hook.
  • Return Value: The hook should return a promise that resolves with the fetched data or rejects with an error.

Expected Behavior:

  1. The useMiddleware hook should execute each middleware function in the provided array.
  2. Each middleware function can modify the request parameters, transform the data, or handle errors.
  3. The final promise returned by the hook should resolve with the transformed data or reject with an error, as determined by the middleware functions.
  4. If any middleware function rejects the promise, subsequent middleware functions should not be executed.

Edge Cases to Consider:

  • Empty middleware array: The hook should execute the data fetching function directly and resolve/reject the promise based on its result.
  • Data fetching function throws an error: The hook should catch the error and reject the promise.
  • Middleware functions throw errors: The hook should catch the error and reject the promise.
  • Asynchronous operations within middleware: Ensure proper handling of asynchronous operations within middleware functions.

Examples

Example 1:

Input:
middleware: [
  (fetchFn, params, resolve, reject) => {
    params.header = 'Authorization: Bearer token';
    fetchFn(params)
      .then(resolve)
      .catch(reject);
  },
  (fetchFn, params, resolve, reject) => {
    fetchFn(params)
      .then(data => resolve(data.map(item => item.name)))
      .catch(reject);
  }
]
fetchFn: () => fetch('/api/users')
Output: Promise resolving with an array of user names after adding an authorization header and mapping the data.
Explanation: The first middleware adds an authorization header to the request. The second middleware maps the response data to an array of user names.

Example 2:

Input:
middleware: [
  (fetchFn, params, resolve, reject) => {
    fetchFn(params)
      .then(data => {
        if (!data.success) {
          reject(new Error("API request failed"));
        } else {
          resolve(data.result);
        }
      })
      .catch(reject);
  }
]
fetchFn: () => fetch('/api/data')
Output: Promise resolving with the 'result' property of the API response if the 'success' property is true, otherwise rejecting with an error.
Explanation: This middleware checks the API response for a 'success' flag and resolves or rejects the promise accordingly.

Example 3: (Edge Case - Empty Middleware Array)

Input:
middleware: []
fetchFn: () => fetch('/api/items')
Output: Promise resolving with the JSON response from '/api/items'.
Explanation: With an empty middleware array, the fetch function is executed directly, and the promise resolves with the data.

Constraints

  • The useMiddleware hook must be implemented using TypeScript.
  • The middleware functions must be executed sequentially.
  • The hook should handle errors gracefully.
  • The hook should be reusable and flexible.
  • The data fetching function should be a function that returns a Promise.
  • The request parameters can be of any type.

Notes

  • Consider using async/await for cleaner asynchronous code within the middleware functions.
  • Think about how to handle errors that occur within the middleware functions themselves.
  • The resolve and reject callbacks are crucial for controlling the promise returned by the hook. Ensure they are used correctly.
  • This challenge focuses on the core logic of the middleware system. You don't need to create a full React component or UI. Just the useMiddleware hook and its associated types.
Loading editor...
typescript