redux 如何从客户端类内部使用分派?

btqmn9zl  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(108)

继续我的冒险,用Zod和Redux围绕axios创建一个客户端/ Package 器,这样客户端就可以在获取和分发所需的状态更新到Redux期间处理错误。
到目前为止,我已经成功地将Zod和验证部分实现到客户机中。现在我想以某种方式将可能的错误状态或成功状态存储到redux中。问题是,无论我如何尝试将dispatch包含到类中,它似乎都没有做任何事情。
目前我的 Package 器客户端是this(在这个示例中没有redux)

interface RequestConfig<T extends z.ZodTypeAny> extends AxiosRequestConfig {
  responseSchema: T;
}

export class ValidatedAxiosClient {
  delete = this.createValidatedAxiosRequest('DELETE');
  get = this.createValidatedAxiosRequest('GET');
  patch = this.createValidatedAxiosRequest('PATCH');
  put = this.createValidatedAxiosRequest('PUT');
  post = this.createValidatedAxiosRequest('POST');

  private unableToFetch = 'error:unableToFetch';

  constructor(private service: string, private baseUrl: string) {}

  private handleNetworkError<T extends z.ZodTypeAny>(
    error: HttpError,
    url: string,
    config: RequestConfig<T>
  ) {
    /**
     * Handle error...
     */
  }

  private handleZodError<T extends z.ZodTypeAny>(
    error: z.ZodError,
    url: string,
    config: RequestConfig<T>
  ) {
    /**
     * Parse and handle ZodError...
     */
  }

  private createValidatedAxiosRequest(method: HTTPMethod) {
    return async <T extends z.ZodTypeAny>(
      url: string,
      config: RequestConfig<T>
    ): Promise<z.infer<T>> => {
      try {

        const response = await axios({ ...config, method, url, baseURL: this.baseUrl });
        return config.responseSchema.parse(response);
      
      } catch (error: any) {

        if (error instanceof z.ZodError) {
          console.log(JSON.stringify({ error }, undefined, 4));
          this.handleZodError<T>(error, url, config);
        }

        if (error instanceof HttpError) {
          this.handleNetworkError<T>(error, url, config);
        }

      }
    };
  }
}

我使用redux-thunk中的ThunkAction,如下所示

const WrappedClient = new ValidatedAxiosClient('UserService', 'http://localhost:3000')

export fetchUser =
  (userId: string): ThunkAction<void, RootState, unknown, Action<string>> =>
  async (dispatch, getState) => {
    if (getState().users.status === 'pending') return;

    dispatch(startedUsersLoading());

    const { name, age } = await WrappedClient.get('/user', {
      params: { userId },
      responseSchema: z.object({
        name: z.string(),
        age: z.number()
      })
    });

    dispatch(getUsersSuccess);
  }

以前我是在客户机外部和fetchUser函数中设置加载和错误状态的。然而,由于我有几个服务,因此客户端,这将是非常重复和相当烦人的管理。我想把州派遣转移到客户端本身。这可能吗
我试图将分派传递到客户机中的createValidatedAxiosRequest中,但分派从未在那里被调用。

private createValidatedAxiosRequest(method: HTTPMethod) {
    return async <T extends z.ZodTypeAny>(
      url: string,
      dispatch: any,
      config: RequestConfig<T>
    ): Promise<z.infer<T>> => {
      try {

        dispatch(LoadingStateForThisService)
        
        const response = await axios({ ...config, method, url, baseURL: this.baseUrl });
        const parsedResponse = config.responseSchema.parse(response);
        
        dispatch(LoadingSuccess);

        return parsedResponse;
      
      } catch (error: any) {

        dispatch(someErrorStateSettingAction); // This is never called
        if (error instanceof z.ZodError) {
          console.log(JSON.stringify({ error }, undefined, 4));
          this.handleZodError<T>(error, url, config);
        }

        if (error instanceof HttpError) {
          this.handleNetworkError<T>(error, url, config);
        }

      }
    };
  }

我想知道我是否应该将分派传递给构造函数,但这也变得很奇怪,因为这样我就需要在每次调用fetch函数时创建一个新的客户端示例

export fetchUser =
  (userId: string): ThunkAction<void, RootState, unknown, Action<string>> =>
  async (dispatch, getState) => {
    if (getState().users.status === 'pending') return;

    const WrappedClient = new ValidatedAxiosClient('UserService', dispatch, 'http://localhost:3000') // NOPE!! Don't want to instantiate this every time the function is called

    dispatch(startedUsersLoading());

    const { name, age } = await WrappedClient.get('/user', {
      params: { userId },
      responseSchema: z.object({
        name: z.string(),
        age: z.number()
      })
    });

    dispatch(getUsersSuccess);
  }

有没有一种方法可以实现我正在尝试做的事情,或者我应该尝试一些不同的方法?

ctzwtxfj

ctzwtxfj1#

我的建议是在这个ValidatedAxiosClient类对象示例中传递并存储应用程序的Redux存储的示例。这将允许ValidatedAxiosClient不仅在必要时访问任何当前状态值,而且还可以将操作分派到存储。
示例:

import { store } from '../path/to/store';

type AppStore = typeof store;

export class ValidatedAxiosClient {
  delete = this.createValidatedAxiosRequest('DELETE');
  get = this.createValidatedAxiosRequest('GET');
  patch = this.createValidatedAxiosRequest('PATCH');
  put = this.createValidatedAxiosRequest('PUT');
  post = this.createValidatedAxiosRequest('POST');

  private unableToFetch = 'error:unableToFetch';

  constructor(
    private service: string,
    private baseUrl: string,
    private store: AppStore, // <-- app store reference
  ) {}

  private handleNetworkError<T extends z.ZodTypeAny>(
    error: HttpError,
    url: string,
    config: RequestConfig<T>
  ) {
    /**
     * Handle error...
     */
  }

  private handleZodError<T extends z.ZodTypeAny>(
    error: z.ZodError,
    url: string,
    config: RequestConfig<T>
  ) {
    /**
     * Parse and handle ZodError...
     */
  }

  private createValidatedAxiosRequest(method: HTTPMethod) {
    return async <T extends z.ZodTypeAny>(
      url: string,
      config: RequestConfig<T>
    ): Promise<z.infer<T>> => {
      try {

        this.store.dispatch(LoadingStateForThisService());

        const response = await axios({
          ...config,
          method,
          url,
          baseURL: this.baseUrl
        });
        const parsedResponse = config.responseSchema.parse(response);
        
        this.store.dispatch(LoadingSuccess());

        return parsedResponse;
      } catch (error: any) {
        this.store.dispatch(someErrorStateSettingAction());

        if (error instanceof z.ZodError) {
          console.log(JSON.stringify({ error }, undefined, 4));
          this.handleZodError<T>(error, url, config);
        }

        if (error instanceof HttpError) {
          this.handleNetworkError<T>(error, url, config);
        }
      }
    };
  }
}
import { store } from '../path/to/store';

const WrappedClient = new ValidatedAxiosClient(
  'UserService',
  'http://localhost:3000',
  store,
);

如果你正在寻找一个替代方案,或建议/推荐,那么我也强烈建议你看看Redux-Toolkit Query。您没有定义一个带有通用HTTP方法的类并微观管理所有加载/获取状态/状态,而是定义了一个API切片来定义应用程序使用的端点,Redux-Toolkit * 自动 * 处理加载/获取状态并生成应用程序将使用的查询钩子,并且它集成到应用程序使用的Redux商店中。

相关问题