javascript - 如何避免在使用 easy-peasy redux 库时导入商店时发生的循环依赖?
问题描述
创建商店后导入商店会导致此问题。
store.ts
/**
* Author: Rahul Shetty
*
* The central redux store of our app is created and exported to be used from
* here.
*/
import { createStore, persist } from 'easy-peasy';
import { services } from '@services/index';
import storage from '@utils/storage';
import { Entities } from 'types/entities';
import storeModel from '@models/index';
// Add any additional store enhancers
let storeEnhancers: any[] = [];
if (__DEV__) {
const reactotron = require('./reactotron.config').default;
// @types/ws
const reactotronConfig = reactotron();
// Global variable. Use it to log your variable and you can see the result in reactotrons
(global as any).tronlog = (value: any) => reactotronConfig.log('TRON', value);
storeEnhancers = [...storeEnhancers, reactotronConfig.createEnhancer()];
}
export const store = createStore(
persist(storeModel, {
whitelist: [Entities.LANGUAGES],
storage,
}),
{
injections: { ...services },
enhancers: [...storeEnhancers],
},
); // create our store
if (__DEV__) {
// @types/webpack-env
if (module.hot) {
// At times the app breaks. Just reload and start again
module.hot.accept('../models', () => {
store.reconfigure(storeModel); // Here is the magic
});
}
}
export default store;
上面代码片段中要注意的重要行是injections: { ...services }
. 它将所有服务作为用于进行 API 调用的函数注入。
服务/index.ts
import * as placesServices from './places';
import * as appointmentServices from './appointment';
export const services = {
placesServices,
appointmentServices,
};
服务/places.ts
import { PlaceServices } from 'types/places';
import { customerAPIInstance } from './api';
export const getPlaces: PlaceServices['getPlaces'] = () =>
customerAPIInstance.get('/branches');
export const favoritePlace: PlaceServices['favoritePlace'] = (info) =>
customerAPIInstance.put('/favorite', info);
下面给出的代码片段创建了一个依赖循环。
服务/api.ts
/**
* Author: Rahul Shetty
*
* API Wrapper for the app
*/
import Config from 'react-native-config';
import axios, { Method, AxiosInstance } from 'axios';
import { Entity } from 'types/entities';
import store from '@store/index';
import { ActionCreator } from 'easy-peasy';
import { MetaPayload } from 'types/meta';
type StoreOptions = {
setPending: ActionCreator<MetaPayload>;
setError: ActionCreator<MetaPayload>;
resetError: ActionCreator<MetaPayload>;
};
type APIOptions = {
method: Method;
url: string;
data: DynamicObject;
entity?: Entity;
};
const { BASE_URL } = Config;
export const customerAPIInstance = axios.create({
baseURL: BASE_URL,
timeout: 20000,
headers: { 'Content-Type': 'application/json' },
});
export const apiConfig = (
apiInstance: AxiosInstance,
storeOptions: StoreOptions,
) => async <T>(apiOptions: APIOptions): Promise<T> => {
const { method, url, data, entity } = apiOptions;
const { setPending, setError, resetError } = storeOptions;
/**
* if the developer doesn't wanna track the asynchronous states, then we avoid
* calling store actions by not passing the entity name
*/
if (entity) {
setPending({
pending: true,
entity,
});
}
try {
const result = await apiInstance({
method,
url,
data,
});
// Reset any error if the API call was successful
if (entity) {
resetError({
entity,
});
}
return result.data;
} catch (err) {
const message =
err.response && err.response.data && err.response.data.message
? err.response.data.message
: err.message;
// Save the error related data if the API call was unsuccessful
if (entity) {
setError({
error: {
message,
statusCode: err.status || 500,
},
entity,
});
}
throw Error(err);
} finally {
// The API call has either successfully resolved or has been rejected.
// In either case, pending should be set to false
if (entity) {
setPending({
pending: false,
entity,
});
}
}
};
export const CustomerAPI = apiConfig(customerAPIInstance, {
setPending: store.getActions().metadata.setPending,
setError: store.getActions().metadata.setError,
resetError: store.getActions().metadata.resetError,
});
正如您可能在上面可用的代码片段中观察到的那样,我正在尝试使用可通过商店获得的简单易用的redux 操作从单点处理错误和加载状态。
具体来说,import store from '@store/index';
创建依赖循环。
但是,由于注入只不过是反过来使用存储的服务,因此形成了依赖循环。
存储 -> 注入 -> 服务 -> 地点 -> API 实例 -> 存储
我确实有解决办法。我可以通过调用服务的方法传递动作。例如,
模型/地点.ts
const placesModel = {
fetchPlaces: thunk(async(actions, payload, { injections, getStoreActions }) => {
injections.getPlaces(payload, getStoreActions);
})
};
但是,使用上面显示的方法,我必须继续将存储操作作为第二个参数传递给所有服务。
如何通过共享存储操作来打破依赖循环,以确保 API 实例可以从单个位置设置错误和加载状态?
解决方案
这里的主要问题是导入store.ts
文件,因为它依赖于服务。我进行了以下更改以解决此问题。
我创建async-handler.ts
并添加了以下代码:
import { Entity } from 'types/entities';
import type { Actions, Meta, State, Dispatch } from 'easy-peasy';
import { Services } from 'types/services';
import { StoreModel } from 'types/model';
// @TODO: Simplify the repetitive types
type HandleAsyncStates = <Model extends object = {}, Payload = void>(
callback: (
actions: Actions<Model>,
payload: Payload,
helpers: {
dispatch: Dispatch<StoreModel>;
getState: () => State<Model>;
getStoreActions: () => Actions<StoreModel>;
getStoreState: () => State<StoreModel>;
injections: Services;
meta: Meta;
},
) => Promise<any>,
) => (
actions: Actions<Model>,
payload: Payload,
helpers: {
dispatch: Dispatch<StoreModel>;
getState: () => State<Model>;
getStoreActions: () => Actions<StoreModel>;
getStoreState: () => State<StoreModel>;
injections: Services;
meta: Meta;
},
) => Promise<any>;
export const handleAsyncStates: HandleAsyncStates = (callback) => async (
actions,
payload,
helpers,
) => {
const { getStoreActions, meta } = helpers;
const { setPending, resetError, setError } = getStoreActions().metadata;
const entity = meta.path[0] as Entity;
// Start the pendinng state
setPending({
pending: true,
entity,
});
try {
// Perform you API or async tasks inside the callback, and return the promise
const result = await callback(actions, payload, helpers);
// reset any error if it has been set
resetError(entity);
return result;
} catch (err) {
const message =
err.response && err.response.data && err.response.data.message
? err.response.data.message
: err.message;
const errData = {
message,
statusCode: err.status || 500,
};
// Save the error related data in the store if the API call was unsuccessful
setError({
error: errData,
entity,
});
return errData;
} finally {
// The API call has either successfully resolved or has been rejected.
// In either case, pending should be set to false
setPending({
pending: false,
entity,
});
}
};
这个充当 HOC 的异步处理程序然后被传递给存储 thunk,如下所示:
fetchAndSaveNearbyPlaces: thunk(
handleAsyncStates<PlacesModel, PlaceAPIOption>(
async (actions, payload, { injections }) => {
const { placesServices } = injections;
const response = await placesServices.getPlaces<PlaceListResponse>();
actions.saveNearbyPlaces(response.data.result);
return response;
},
),
),
存储 thunk 提供了执行常见存储操作(例如跟踪 API 异步状态)所需的必要操作和服务,并且完全无需导入store.ts
。
推荐阅读
- reactjs - 大家好!我不能在一个状态中结合两个状态。我为输入值创建了一种状态,为图像上传器创建了一种状态
- laravel - Laravel Dusk 没有在失败时截图
- c# - 从命令行运行 ASP NET Core
- javascript - 地理位置自动刷新页面
- com - WdInformation.wdVerticalPositionRelativeToPage 在 VBA 和 C# 中给出不同的值
- php - 确保 0 和逗号保留在变量中而不使其成为字符串?PHP
- android - 在 MVVM 架构中格式化来自改造 API 的嵌套 JSON 响应 - Kotlin
- java - 使用 xpath 编辑 xml 上的值会导致 java 中的 nullpointerexception
- c - 为什么我的大多维数组会出现分段错误(核心转储)?
- azure - Azure 函数可以自行触发吗?