@loona/react
Version:
App State Management done with GraphQL (react integration)
124 lines (109 loc) • 2.69 kB
text/typescript
import {ApolloClient, MutationOptions} from 'apollo-client';
import {
Manager,
StateClass,
METADATA_KEY,
isMutation,
getMutation,
isMutationAsAction,
getNameOfMutation,
MutationObject,
buildContext,
Metadata,
EffectMethod,
Action,
ActionObject,
EffectContext,
withUpdates,
buildGetCacheKey,
getActionType,
buildActionFromResult,
buildActionFromError,
} from '@loona/core';
import {FetchResult} from 'apollo-link';
export class Loona {
effects: Record<string, Array<EffectMethod>> = {};
constructor(
private client: ApolloClient<any>,
public manager: Manager,
states: StateClass[],
) {
manager.getClient = () => client;
states.forEach((state: any) => {
const instance = new state();
const meta: Metadata = state[METADATA_KEY];
this.manager.addState(instance, meta);
this.addEffects(instance, meta.effects);
});
}
dispatch(action: MutationObject | ActionObject): void {
if (isMutation(action)) {
const mutation = getMutation(action);
const config = {
mutation,
...action,
};
this.wrapMutation(
this.client.mutate(withUpdates(config, this.manager)),
config,
);
} else {
this.runEffects({
type: getActionType(action),
...action,
});
}
}
addEffects(instance: any, meta?: Metadata.Effects): void {
if (!meta) {
return;
}
for (const type in meta) {
if (!this.effects[type]) {
this.effects[type] = [];
}
meta[type].forEach(({propName}) => {
this.effects[type].push(instance[propName].bind(instance));
});
}
}
runEffects(action: Action) {
let type = action.type;
const cache = this.client.cache;
const context: EffectContext = {
...buildContext(
{
cache,
getCacheKey: buildGetCacheKey(cache),
},
this.client,
),
dispatch: this.dispatch.bind(this),
};
if (isMutationAsAction(action)) {
type = getNameOfMutation(action.options.mutation);
}
const effectsToRun = this.effects[type];
if (effectsToRun) {
effectsToRun.forEach(effect => {
effect(action, context);
});
}
}
public wrapMutation<T>(
mutationPromise: Promise<FetchResult<T>>,
config: MutationOptions<T, any>,
shouldThrow = true,
): void {
mutationPromise
.then(result => {
this.runEffects(buildActionFromResult(config, result));
})
.catch(error => {
this.runEffects(buildActionFromError(config, error));
if (shouldThrow) {
throw error;
}
});
}
}