Skip to content

Commit

Permalink
enhance: Simplify action shapes (#3143)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker committed Jul 12, 2024
1 parent 01b5908 commit f4cf8a4
Show file tree
Hide file tree
Showing 67 changed files with 670 additions and 605 deletions.
6 changes: 6 additions & 0 deletions .changeset/hip-queens-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@data-client/core': minor
'@data-client/react': patch
---

action.meta.args -> action.args
32 changes: 32 additions & 0 deletions .changeset/hot-kangaroos-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
'@data-client/core': minor
---

Add `actions` export

`actions` is a namespace for all action creators. It is typically
preferred to use [Controller's](https://dataclient.io/docs/api/Controller) type-safe dispatch methods, as
members of this namespace could have breaking changes in a minor release.

```ts
import { actions, type Manager, type Middleware } from '@data-client/core';

export default class MyManager implements Manager {
getMiddleware = (): Middleware => controller => next => {
const todo = { id: '5', title: 'my first todo' };

// These do the same thing
controller.dispatch(
actions.createSet(Todo, { args: [{ id: todo.id }], value: todo }),
);
// This is simpler; type-enforced; and will only change in major versions
controller.set(Todo, { id: todo.id }, todo);

return async action => next(action);
};

cleanup() {}
}
```

BREAKING CHANGE: Removed `createFetch`, `createSet`, `createSetResponse` from export. Use action.createFetch instead.
6 changes: 4 additions & 2 deletions .changeset/rich-frogs-move.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ Change normalize() interface
function normalize(
schema,
input,
{ date, expiresAt, fetchedAt, args },
args,
{ entities, indexes, entityMeta },
{ date, expiresAt, fetchedAt },
);
```

Expand All @@ -19,8 +20,9 @@ function normalize(
const { result, entities, indexes, entityMeta } = normalize(
action.endpoint.schema,
payload,
action.meta,
action.args,
state,
action.meta,
);
```

6 changes: 6 additions & 0 deletions .changeset/stupid-worms-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@data-client/core': minor
'@data-client/react': patch
---

action.meta.key -> action.key
10 changes: 5 additions & 5 deletions examples/benchmark/normalizr.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ const queryInfer = queryMemo.buildQueryKey(

let githubState = normalize(User, userData);

const date = Date.now()
const actionMeta = {
fetchedAt: Date.now(),
date: Date.now(),
expiresAt: Date.now() + 10000000,
args: [],
fetchedAt: date,
date,
expiresAt: date + 10000000,
};

export default function addNormlizrSuite(suite) {
Expand All @@ -55,7 +55,7 @@ export default function addNormlizrSuite(suite) {
let curState = initialState;
return suite
.add('normalizeLong', () => {
normalize(ProjectSchema, data, actionMeta, curState);
normalize(ProjectSchema, data, [], curState, actionMeta);
curState = { ...initialState, entities: {}, endpoints: {} };
})
.add('infer All', () => {
Expand Down
4 changes: 2 additions & 2 deletions examples/coin-app/src/resources/StreamManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default class StreamManager implements Manager {
)
break;
if ('channel' in action.endpoint) {
this.subscribe(action.meta.args[0]?.product_id);
this.subscribe(action.args[0]?.product_id);
// consume subscription if we use it
return Promise.resolve();
}
Expand All @@ -79,7 +79,7 @@ export default class StreamManager implements Manager {
this.send(
JSON.stringify({
type: 'unsubscribe',
product_ids: [action.meta.args[0]?.product_id],
product_ids: [action.args[0]?.product_id],
channels: [action.endpoint.channel],
}),
);
Expand Down
2 changes: 1 addition & 1 deletion examples/normalizr-github/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class BaseEntity extends Entity {
id = 0;

pk() {
return `${this.id}`;
return this.id;
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/normalizr-relationships/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class BaseEntity extends Entity {
id = 0;

pk() {
return `${this.id}`;
return this.id;
}
}

Expand Down Expand Up @@ -34,7 +34,7 @@ class Comment extends BaseEntity {
commenter: User,
};

static process(value, parent, key) {
static process(value, parent) {
return { ...value, post: parent.id };
}
}
Expand Down
70 changes: 34 additions & 36 deletions packages/core/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,98 +28,98 @@ type EndpointDefault = EndpointInterface & {
update?: EndpointUpdateFunction<EndpointInterface>;
};

/* SET */
export interface SetMeta {
args: readonly any[];
fetchedAt: number;
date: number;
expiresAt: number;
/** General meta-data for operators */
export interface ActionMeta {
readonly fetchedAt: number;
readonly date: number;
readonly expiresAt: number;
}

/** Action for Controller.set() */
export interface SetAction<S extends Queryable = any> {
type: typeof SET_TYPE;
schema: S;
meta: SetMeta;
args: readonly any[];
meta: ActionMeta;
value: {} | ((previousValue: Denormalize<S>) => {});
}

/* setResponse */
export interface SetResponseMeta {
export interface SetResponseActionBase<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof SET_RESPONSE_TYPE;
endpoint: E;
args: readonly any[];
key: string;
fetchedAt: number;
date: number;
expiresAt: number;
meta: ActionMeta;
}
export interface SetResponseActionSuccess<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof SET_RESPONSE_TYPE;
endpoint: E;
meta: SetResponseMeta;
> extends SetResponseActionBase<E> {
response: ResolveType<E>;
error?: false;
}
export interface SetResponseActionError<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof SET_RESPONSE_TYPE;
endpoint: E;
meta: SetResponseMeta;
> extends SetResponseActionBase<E> {
response: UnknownError;
error: true;
}
/** Action for Controller.setResponse() */
export type SetResponseAction<
E extends EndpointAndUpdate<E> = EndpointDefault,
> = SetResponseActionSuccess<E> | SetResponseActionError<E>;

/* FETCH */
export interface FetchMeta<A extends readonly any[] = readonly any[]> {
args: A;
key: string;
export interface FetchMeta {
fetchedAt: number;
resolve: (value?: any | PromiseLike<any>) => void;
reject: (reason?: any) => void;
promise: PromiseLike<any>;
fetchedAt: number;
}

/** Action for Controller.fetch() */
export interface FetchAction<E extends EndpointAndUpdate<E> = EndpointDefault> {
type: typeof FETCH_TYPE;
endpoint: E;
meta: FetchMeta<readonly [...Parameters<E>]>;
args: readonly [...Parameters<E>];
key: string;
meta: FetchMeta;
}

/* OPTIMISTIC */
/** Action for Endpoint.getOptimisticResponse() */
export interface OptimisticAction<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof OPTIMISTIC_TYPE;
endpoint: E;
meta: SetResponseMeta;
args: readonly any[];
key: string;
meta: ActionMeta;
error?: false;
}

/* SUBSCRIBE */
/** Action for Controller.subscribe() */
export interface SubscribeAction<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof SUBSCRIBE_TYPE;
endpoint: E;
meta: {
args: readonly any[];
key: string;
};
args: readonly any[];
key: string;
}

/** Action for Controller.unsubscribe() */
export interface UnsubscribeAction<
E extends EndpointAndUpdate<E> = EndpointDefault,
> {
type: typeof UNSUBSCRIBE_TYPE;
endpoint: E;
meta: {
args: readonly any[];
key: string;
};
args: readonly any[];
key: string;
}

/* EXPIRY */
Expand All @@ -136,9 +136,7 @@ export interface InvalidateAllAction {

export interface InvalidateAction {
type: typeof INVALIDATE_TYPE;
meta: {
key: string;
};
key: string;
}

/* RESET */
Expand Down
18 changes: 10 additions & 8 deletions packages/core/src/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import {
} from '@data-client/normalizr';

import AbortOptimistic from './AbortOptimistic.js';
import createExpireAll from './createExpireAll.js';
import createFetch from './createFetch.js';
import createInvalidate from './createInvalidate.js';
import createInvalidateAll from './createInvalidateAll.js';
import createReset from './createReset.js';
import createSet from './createSet.js';
import createSetResponse from './createSetResponse.js';
import {
createUnsubscription,
createSubscription,
} from './createSubscription.js';
} from './actions/createSubscription.js';
import {
createExpireAll,
createFetch,
createInvalidate,
createInvalidateAll,
createReset,
createSet,
createSetResponse,
} from './actions/index.js';
import ensurePojo from './ensurePojo.js';
import type { EndpointUpdateFunction } from './types.js';
import { initialState } from '../state/reducer/createReducer.js';
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/controller/actions/createExpireAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { EXPIREALL_TYPE } from '../../actionTypes.js';
import type { ExpireAllAction } from '../../types.js';

export function createExpireAll(
testKey: (key: string) => boolean,
): ExpireAllAction {
return {
type: EXPIREALL_TYPE,
testKey,
};
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import type { EndpointInterface, NI } from '@data-client/normalizr';

import { EndpointUpdateFunction } from './types.js';
import { FETCH_TYPE } from '../actionTypes.js';
import type { FetchAction, FetchMeta } from '../types.js';
import { FETCH_TYPE } from '../../actionTypes.js';
import type { FetchAction, FetchMeta } from '../../types.js';
import { EndpointUpdateFunction } from '../types.js';

/**
* Requesting a fetch to begin
*/
export default function createFetch<
export function createFetch<
E extends EndpointInterface & { update?: EndpointUpdateFunction<E> },
>(
endpoint: E,
{ args }: { args: readonly [...Parameters<E>] },
): FetchAction<E> {
const key = endpoint.key(...args);
let resolve: (value?: any | PromiseLike<any>) => void = 0 as any;
let reject: (reason?: any) => void = 0 as any;
const promise = new Promise<any>((a, b) => {
[resolve, reject] = [a, b];
});
const meta: FetchMeta<typeof args> = {
args,
key,
const meta: FetchMeta = {
fetchedAt: Date.now(),
resolve,
reject,
promise,
fetchedAt: Date.now(),
};

return {
type: FETCH_TYPE,
meta,
endpoint,
args,
key: endpoint.key(...args),
meta,
};
}
14 changes: 14 additions & 0 deletions packages/core/src/controller/actions/createInvalidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { EndpointInterface } from '@data-client/normalizr';

import { INVALIDATE_TYPE } from '../../actionTypes.js';
import type { InvalidateAction } from '../../types.js';

export function createInvalidate<E extends EndpointInterface>(
endpoint: E,
{ args }: { args: readonly [...Parameters<E>] },
): InvalidateAction {
return {
type: INVALIDATE_TYPE,
key: endpoint.key(...args),
};
}
11 changes: 11 additions & 0 deletions packages/core/src/controller/actions/createInvalidateAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { INVALIDATEALL_TYPE } from '../../actionTypes.js';
import type { InvalidateAllAction } from '../../types.js';

export function createInvalidateAll(
testKey: (key: string) => boolean,
): InvalidateAllAction {
return {
type: INVALIDATEALL_TYPE,
testKey,
};
}
Loading

0 comments on commit f4cf8a4

Please sign in to comment.