Open vicary opened 1 year ago
@redbar0n You may be interested on this one.
Proxies sounds good. The implementation goes over my head, though. Will probably have more opinions and suggestions as far as the API goes.
user.name.$skip({ if: showName })
looks particularly neat! Compared to previous suggestion in https://github.com/gqty-dev/gqty/issues/448 :
user.name({ '@skip': { if: showName } }) // from the suggestion in issue 448 which was: u.member({ '@skip': { if: true } })
But what about duplicate directives on the same field, which the GraphQL spec allows, as you mentioned previously. So this previous suggestion:
u.name({ '@skip': { if: skipName }, ‘@include’: { if: true } }),
Could maybe be chained like this instead?
user.name.$skip({ if: showName }).$include({ if: includeName })
Btw, the React PoC gives:
Error in /turbo_modules/react-dom@18.1.0/cjs/react-dom.development.js (14757:9)
Objects are not valid as a React child (found: object with keys {}).
If you meant to render a collection of children, use an array instead.
@redbar0n Sorry I forgot to fork again before I start testing for nullables, the React PoC should be working now.
Directives API should be easy if we have primitives sorted out, the main concern is whether people find primitive wrapper classes surprising. i.e. query.me.name === "John Doe" // -> false
and you have to call .valueOf()
like this query.me.username.valueOf() === "John" // -> true
.
This problem could be particularly bad for booleans, null
and undefined
.
Could this work?
query.me.username.value
query.me.username.directives
I feel like I've seen similar type of API's in JS before, maybe in data or request objects, or maybe on the frontend with stuff like document.forms["username"]["user"].value
Vanilla conventions such as .valueOf()
, .toString()
and Symbol.toPrimitive
would be better, it helps type coercion.
I like the chaining API for directives, $skip({ if: true })
just feels more like @skip(if: true)
in GraphQL.
I am wondering if users would find primitive objects surprising, consider the following example:
function Component() {
return (
<>
{ // This will ALWAYS render
query.me.isVerified && <h1>Verified</h1>
}
{ // This renders correctly
query.me.isVerified.valueOf() && <h1>Verified</h1>
}
</>
);
}
The Verified
text would always render because isVerified
is the object Boolean(false)
instead of primitive false
.
Just a thought: If the underlying implementation is a proxy object, then maybe it is best to use an object as the API instead of chaining, to reduce the impedance mismatch?
Inspired by reading: "Passing a shared value without .value getter causes Reanimated to watch for shared value changes." https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/animations/#animations-in-inline-styles
@redbar0n How would you like to add directives with object as the API?
@vicary ah, I see how it was ambiguous. I was thinking about passing in an object to a function, instead of chaining. But that only works for setting the proxy state, not retrieving it.
I am wondering if users would find primitive objects surprising, consider the following example:
Yes, I think so. I would for sure.
I'm coming back to my suggestion here https://github.com/gqty-dev/gqty/issues/448#issuecomment-997466839 :
Most important presumption here is that all fields would always have to be functions (which again returns the value).
I think that would solve it, because then you'd have full control over what the function returns. The only thing the user would have to remember is to always end the chaining with a ()
e.g. query.me.isVerified()
Summary
Just had a rough idea of primitive proxies while I am refactoring, PoC works so far.
This, albeit a bit hacky, allows us to add custom methods into scalars while retaining all existing primitive methods.
This affects all existing scalars, please let us know what you think and whether you have conflicting implementations.
Intended use cases
Directives
(This is only an example, API not decided yet)
Custom GraphQL Scalars
Besides directives, I am also planning to treat this as an extension point for custom GraphQL scalars (graphql-scalars) in the future.
Questions
null
andundefined