Comments (4)
What do you think about this API?
e.g. overload for pessimistic update:
export type PessimisticUpdateConfig<Data, Response> = {
mode: 'pessimistic',
query: Query<any, Data, any>,
mutation: Mutation<any, Response, any>
} & (Response extends Data ? {
mapResponse?: (response: Response) => Data
} : {
mapResponse: (response: Response) => Data
});
export function update<Data, Response>(config: PessimisticUpdateConfig<Data, Response>): void {/*...*/}
from farfetched.
I think it is also probably a good idea to allow 'general' updates. In other words - manual updates.
I am thinking something along the lines of
export type GeneralUpdateConfig<Data, EventPayload> = {
mode: 'general',
query: Query<any, Data, any>,
fn: (payload: EventPayload) => Data
};
export function update<Data, EventPayload>(config: GeneralUpdateConfig<Data, EventPayload>): Event<EventPayload> {/*...*/}
should suffice.
One can always do anything he wants with the received event.
from farfetched.
Moving the gist contents here for further discussion:
RFC: update
operator for @farfetched/core
farfetched
already knows how to work with mutations.
But they are useless without a way to update any Query based on result of those mutations or on their params (also known as pessimistic
and optimistic
updates).
I think having an operator that provides this ability will be a perfect and much needed addition for library.
API overview
I propose the next api to update queries via mutations:
import {
Mutation,
Query,
TwoArgsDynamicallySourcedField,
ThreeArgsDynamicallySourcedField
} from '@farfetched/core';
type ExcludeVoid<T> = Pick<
T,
{
[K in keyof T]: T[K] extends void ? never : K;
}[keyof T]
>;
type MapperBaseArgs<Data, MutationParams> = {
queryData: Data;
mutationParams: MutationParams;
};
type Extends<Specific, Wide> = Specific extends Wide ? true : false;
type Conditional<Condition, T> = Condition extends true
? Partial<T>
: Required<T>;
type BasicUpdateConfig<Data, QueryParams, MutationParams, Response, Error> = {
query: Query<QueryParams, Data, any>;
mutation: Mutation<MutationParams, Response, Error>;
fromError?: (
args: ExcludeVoid<
MapperBaseArgs<Data, MutationParams> & {
error: Error;
pristineData?: Data;
}
>
) => Data;
};
export type PessimisticUpdateConfig<
Data,
QueryParams,
MutationParams,
Response,
Source,
Error
> = BasicUpdateConfig<Data, QueryParams, MutationParams, Response, Error> &
Conditional<
Extends<Response, Data>,
{
map: ThreeArgsDynamicallySourcedField<
Data,
{ queryParams: QueryParams; mutationParams: MutationParams },
Response,
Data,
Source
>;
}
>;
export type OptimisticUpdateConfig<
Data,
QueryParams,
MutationParams,
Response,
Source,
RefetchSource,
Error
> = (BasicUpdateConfig<Data, QueryParams, MutationParams, Response, Error> & {
refetch?: ThreeArgsDynamicallySourcedField<
Data,
{ queryParams: QueryParams; mutationParams: MutationParams },
Error,
{ params: QueryParams },
RefetchSource
>;
}) &
Conditional<
Extends<MutationParams, Data>,
{
map: TwoArgsDynamicallySourcedField<Data, MutationParams, Data, Source>;
}
>;
export function pessimisticUpdate<
Data,
QueryParams,
MutationParams,
Response,
Source,
Error
>(
config: PessimisticUpdateConfig<
Data,
QueryParams,
MutationParams,
Response,
Source,
Error
>
): void {}
export function optimisticUpdate<
Data,
QueryParams,
MutationParams,
Response,
Source,
RefetchSource,
Error
>(
config: OptimisticUpdateConfig<
Data,
QueryParams,
MutationParams,
Response,
Source,
RefetchSource,
Error
>
): void {}
By having this api it seems like we can cover every possible use case for updates.
Such as:
- Updating query data immediately after successful mutation
- Updating query data immediately after beginning of a mutation
- Updating query data after mutation ended with an error
- Local updates (developer just needs to add a local mutation)
- Error handling
- Updates based on {mutation params, old data, mutation response, error}
Example usage
const query = createQuery({
handler: async (x: number) => ({
id: x,
name: '',
}),
});
const mutation = createMutation({
effect: createEffect(async (_: { id: string; name: number }) => ({
id: 2,
})),
});
pessimisticUpdate({
query,
mutation,
map: {
source: createStore('literal' as const),
fn(queryData, params, response, source) {
return {
id: ++response.id,
name: source,
};
},
},
fromError({error, pristineData}) {
console.warn(`mutation ended with an error: ${error.message}`);
function createExpectedData(): NonNullable<typeof pristineData> {
/* ... */
}
const origData = pristineData ?? createExpectedData();
return origData
}
});
optimisticUpdate({
query,
mutation,
map(queryData, mutationParams) {
return {
id: parseInt(mutationParams.id) + 1,
name: mutationParams.name.toString(),
};
},
refetch: {
source: createStore(42 as const),
fn(queryData, { queryParams, mutationParams }, error, source) {
return '';
},
// ^ Type 'string' is not assignable to type '{ params: number; }'.
},
});
from farfetched.
I assume optimistic and pessimistic updates are entirely different cases, let's split them to different PR's.
We can release update
(just regular pessimistic update) in the next release, however for the optimisitcUpdate
we have to solve plenty of different tasks.
update
I love the proposed API, it looks like it could solve all cases that I can imagine. There are couple minor question:
map
butfromError
looks weird for me, should we keep them consistent, likefromData
andfromError
? Or maybe it would be better to something likebySuccess
andbyFailure
? It could lead us to read this operatorupdate query originQuery after mutation changeOriginMutation bySuccess (...rule) and byFailure (...ruke)
.- four arguments of
fn
inmap
looks a little bit harsh. Perhaps something likefn(query: { data }, mutation: { params, data }, source)
would be more readable? pristineData
in pessimistic update does not have any sense because in case of error Query data would not be updated, so it is justdata
from Query- I assume
error
infromError
based on Mutation error, so I purpose to move it to second arg to be consistent withmap
. - How can we make Query re-start after update? The case is simple, we are logging-in user and response of
loginMutation
returns onlyemail
, butuserQuery
has optionalname
that could be retrieved only while re-executing.
optimisticUpdate
All question from the previous section is relevant there too, furthermore I have some other issues with this case:
-
Race. There is no API for solving such case:
-> start mutation 1
-> query updated, get state 1, old state 0 is saved somewhere for rollback
-> start mutation 2
-> query updated, get state 2, old state 1 is saved somewhere for rollback
-> fail mutation 1
-> what's next? -
refetch
args have to be consistent with other callbacks. Like(query: { params, data }, mutation: { params, error }, source)
. -
I am not ok with the term
pristineData
🤔
However, in general, I love this API 💙
from farfetched.
Related Issues (20)
- Add priority field for `createJsonQuery` and `createJsonMutation` HOT 1
- Can't pass to connectQuery.target query with initialData
- Add names for internal units
- Add new Statement: No globals HOT 3
- Conditional `cache`
- TypeError: params.valueOf is not a function HOT 2
- How to debug sha1 keys? HOT 2
- Query/Mutation usage as effect HOT 3
- Implement reset method for mutations HOT 1
- types of operator update HOT 1
- Сoncurrency.abort field changes behavior of Query.abort event HOT 1
- Proper `Accept` header should be provided in `createJsonQuery` HOT 1
- Unexpected behaviour of $status and $pending HOT 1
- double query returns $pending = false HOT 2
- `source` in `mapData` does not trigger query `$data` recalculation
- `update` operator's type improvements HOT 2
- Cloudflare workers Error: The 'credentials' field on 'RequestInitializerDict' is not implemented HOT 1
- Cloudflare worker `request.clone()` error
- Add opportunity to directly assign `undefined` for optional properties HOT 1
- `CacheAdapter.__.$instance` should be `StoreWritable` HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from farfetched.