-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Modern] Client schema docs and examples #1656
Comments
👍 I was trying to find any reference to how to do "client schema extensions" but failed. |
My suspicion is it'll happen in an example in RelayExamples before hitting official documentation here. I don't have a timeline, but that's your best bet for taking advantage of this feature. It already works (at least, it works on my machine :)), and you can find references to it via things like the RelayFilterDirectivesTransform, but getting this to work outside the FB environment hasn't yet happened. |
I would like to know more about this to as well. This is what I figured out till now
Am I missing anything else? |
I tried documenting this the other day and realized that there is an issue with the OSS compiler that prevents schema extensions from being defined. Once this is fixed we can document this feature. Cc @kassens |
@unirey The idea is that you can do something like this:
The missing part is that the oss compiler binary doesn't expose a way to tell the compiler about the client schema - all the primitives exist in the compiler to support client fields, and the runtime supports them as regular fields. The client schema can also define new types: you can do
|
Hi there |
FYI anyone new to react / relay looking for a temporary workaround, a "dirty" solution as suggested in #1787 is to use context at the application root |
@josephsavona In terms of changes, we're looking at :
We currently only have |
@chadfurman The compiler is configured to treat |
@josephsavona Im trying to follow the discussion. Are the steps you mentioned only ones left?
This is still missing? I don't even know what |
@adjourn I'd guess this is the OSS compiler: https://github.com/facebook/relay/tree/master/packages/relay-compiler |
This would be a super helpful feature. |
Does the oss compiler support client schema extensions yet? |
Anyone who didn't notice this thread, also, it has some useful details w.r.t. client schema: #1787 I've been storing lots of client-side data either directly in component state, or as user preferences in the DB if it needs to be shared with other components |
@jstejada any docs about this part? |
Hey @sibelius, unfortunately we haven't made any docs about this yet. The limitation mentioned earlier in this thread still stands. We'll keep you posted with any updates :) |
This is roughly what needs to happen in the compiler: kassens@a51f9f2 That would start reading
This extra field can then be queried as usual using fragment containers and updated using
(I'm typing this from memory and haven't actually tested this, but given there's a bunch of interest and this has been too long on the back burner for us, I figured someone might be interested to take a look). |
@kassens @brysgo did this to make client extensions works: https://github.com/brysgo/create-react-app/blob/master/packages/react-scripts/scripts/relay.js#L75 does this look reasonable? here a sample example of it https://github.com/brysgo/relay-example |
@sibelius just mixing the client schema into the server schema won’t work: the compiler needs to know which fields/types are from which schema in order to strip out client-only portions of queries that it sends to the server. Th compiler binary needs to change to accept a second argument (eg |
Check the network layer in the example, it filters the local stuff. There is probably a better way, but it works. |
My example has been updated to use @edvinerikson's changes in #2264 if anyone is here looking for an example. |
any PR's to add docs for this would be accepted! The only important thing to note would be that client schema extensions only currently support additions to existing types and not adding entirely new types |
What is the good way to prevent records to be collected from store? i.e. commitLocalUpdate(environment, s => {
const err = s.create('4', 'Error');
err.setValue('4', 'id');
err.setValue('Error message', 'message');
root.setLinkedRecords([err], 'errors');
});
But If I add code below store.retain({
dataID: '4',
node: { selections: [] },
variables: {},
}); this prevents records deletion |
As I am reading through this issue it is not clear to me whether relay-compiler version 1.5.0 which was updated 2 months ago on npm supports client schema or not. The release notes for version 1.5.0 mention client schema but running |
It supports client schemas well, we use this feature in development. |
Could anyone provide some meaningful examples, since docs are lacking? |
import { commitLocalUpdate, ROOT_ID } from 'relay-runtime';
const ROOT_TYPE = '__Root';
const ERR_TYPE = 'Error';
const TRACE_TYPE = 'Trace';
function addErrs(environment, errs) {
commitLocalUpdate(this.environment, s => {
let root = s.get(ROOT_ID);
if (!root) {
root = s.create(ROOT_ID, ROOT_TYPE);
}
const errRecords = errs.map(err => {
const errRecord = s.create(err.id, ERR_TYPE);
errRecord.setValue(err.id, 'id');
errRecord.setValue(err.message, 'message');
errRecord.setValue(err.prettyMessage, 'prettyMessage');
errRecord.setValue(err.operationName, 'operationName');
errRecord.setValue(err.path, 'path');
/*
file: String
line: Float
what: String
addr: String
*/
const traceRecords = err.trace.map(traceLine => {
const traceLineRecord = s.create(traceLine.id, TRACE_TYPE);
traceLineRecord.setValue(traceLine.id, 'id');
traceLineRecord.setValue(traceLine.file, 'file');
traceLineRecord.setValue(traceLine.line, 'line');
traceLineRecord.setValue(traceLine.what, 'what');
traceLineRecord.setValue(traceLine.addr, 'addr');
return traceLineRecord;
});
errRecord.setLinkedRecords(traceRecords, 'trace');
return errRecord;
});
const existingErrRecords = root.getLinkedRecords('errors') || [];
root.setLinkedRecords([...errRecords, ...existingErrRecords], 'errors');
});
// Hack to prevent records being disposed on GC
// https://github.com/facebook/relay/issues/1656#issuecomment-380519761
errs.forEach(err => {
const dErr = this.store.retain({
dataID: err.id,
node: { selections: [] },
variables: {},
});
const dTraces = err.trace.map(tLine =>
this.store.retain({
dataID: tLine.id,
node: { selections: [] },
variables: {},
}),
);
this.disposableMap[err.id] = () => {
dErr.dispose();
dTraces.forEach(dT => dT.dispose());
};
});
}
|
Wow, thanks @istarkov , you are the ***** Boss! |
Also we use local type extensions, to add local properties to existing objects like isNew, then setting isNew in mutation updater, we can show that object just created, (updated) etc so you can use local schemes even without commitLocalUpdate |
@istarkov Thanks for that, though, arguably not as straightforward as say, the way Apollo GraphQL does it. And from the looks of it, you can't do things like mutations? |
@MrSaints I haven't tested the ability to have a fully compatible mutation interface. BTW if you can extend mutations (I don't know can you or not (not at computer right now)) it will be not a problem at all, just intercept them at network level and do any stuff with store. |
I found @istarkov findings super useful but didn't know where const dErr = environment.retain({
dataID: err.id,
node: { selections: [] },
variables: {},
}); I also found that you can access store from environment, useful if you want to test changes like the following describe("handleChange(field, value)", () => {
test("updates userForm.user in local Relay store", () => {
const firstName = "Roberto";
const container = shallow(<UserForm relay={{ environment }} />).instance();
container.handleChange("firstName", firstName);
expect(
environment
.getStore()
.getSource()
.get("userForm:user").firstName
).toEqual(firstName);
});
}); Just for the record relay-mock-network-layer is really useful for this kind of tests. |
This post on medium is really helpful too https://medium.com/@matt.krick/replacing-redux-with-relay-47ed085bfafe |
Since @istarkov's comment is a bit dated now I was wondering if there was an easier way to set the initial values for the client state. |
As @mattkrick and @josephsavona are working tough on #2482 - as we might see some improvements soon, it might be good time to start documenting those experimental features on that ### pull request🗡 |
can we have a
|
you can use this helper export const setLocal = (query: GraphQLTaggedNode, localData: object) => {
const request = getRequest(query);
const operation = createOperationDescriptor(request, {});
env.commitPayload(operation, localData);
env.retain(operation.root); // <== here @en_js magic :wink:
}; To make sure relay does not garbage collect same graphql operation |
more blog posts here https://babangsund.com/relay_local_state_management/ anybody want to improve the docs with all this info? |
@sibelius thanks for keeping this alive! Using hooks for local data is pretty easy, too. That way you don't need a clunky QueryRenderer. const data = useLocalQuery<SnackbarQuery>(query) const useLocalQuery = <TQuery extends {response: any; variables: any}>(
environment: Environment,
query: any,
inVariables: TQuery['variables'] = {}
): TQuery['response'] | null => {
const variables = useDeepEqual(inVariables)
const [dataRef, setData] = useRefState<SelectorData | null>(null)
const disposablesRef = useRef<Disposable[]>([])
useEffect(() => {
const {getRequest, createOperationDescriptor} = environment.unstable_internal
const request = getRequest(query)
const operation = createOperationDescriptor(request, variables)
const res = environment.lookup(operation.fragment, operation)
setData(res.data || null)
disposablesRef.current.push(environment.retain(operation.root))
disposablesRef.current.push(
environment.subscribe(res, (newSnapshot) => {
setData(newSnapshot.data || null)
})
)
const disposables = disposablesRef.current
return () => {
disposables.forEach((disposable) => disposable.dispose())
}
}, [environment, setData, query, variables])
return dataRef.current
} |
we now have official docs about how to use client schema extensions: https://relay.dev/docs/en/next/local-state-management thanks to @babangsund for the great work exploring and documenting this |
The "New in Relay Modern" post indicates this support is experimental. This changes the title of the guide to match the announcement. https://relay.dev/docs/en/new-in-relay-modern#client-schema-extensions-experimental As of this writing this feature still seems to be experimental. facebook#2471 (open) facebook#1656 (closed, related)
Any idea when docs and examples would be available?
https://facebook.github.io/relay/docs/new-in-relay-modern.html#client-schema-extensions
The text was updated successfully, but these errors were encountered: