so I am working on relay for react application with server side rendering. I'm trying to use relay to get data from graphql endpoint.
I was kinda following this, using fetchQuery to get data by making request from compiled graphql by relay-compiler.
e.g.
import { graphql, fetchQuery } from 'react-relay'
const query = graphql`
query SomeQuery($id: ID) {
author(id: $id) {
name
...SomeFragment_authorData
}
}
`
const variables = { "id" : "1f" };
fetchQuery(env, query, variables)
.then((jsonData) => { console.log(jsonData); })
when it finishes running, it gives me some sort of this object:
{
author: {
__fragment: { ... }
...
}
}
Which I assume will be used by children components wrapped with createFragmentContainer() to get the real data.
Since I'm not using createFragmentContainer(), I'm not sure how to get the data properly, is there any way to transform above response into the real data?
any help would be appreciated!
Note:
At the moment this is what I do to get the data: env._network.fetch(query(), variables). It is working, but it doesn't seem right that I need to dig into private variable, in order to get the fetch that I provided to Network object.
You need to add a relay directive to your fragment spread:
...SomeFragment_authorData #relay(mask: false)
you can use relay environment and relay-runtime utils.
import {
createOperationDescriptor,
getRequest,
} from 'relay-runtime';
const request = getRequest(query);
const operation = createOperationDescriptor(request, variables);
environment.execute({ operation }).toPromise();
Related
In React server components official GitHub example repo at exactly in this line here they are using response.readRoot().
I want to create a similar app for testing something with RSC's and it seems like the response does not contain the .readRoot() function any more (because they have updated that API in the react package on npm and I cannot find anything about it!). but it returns the tree in value property like below:
This means that whatever I render in my root server component, will not appear in the browser if I render that variable (JSON.parse(value) || not parsed) inside of my app context provider.
How can I render this?
Basically, if you get some response on the client side (in react server components) you have to render that response in the browser which has the new state from server but since I don't have access to readRoot() any more from response, what would be the alternative for it to use?
I used a trick o solve this issue, but one thing to keep in mind is that they are still unstable APIs that react uses and it's still recommended not to use React server component in the production level, uses it for learning and test it and get yourself familiar with it, so back to solution:
My experience was I had a lot of problems with caching layer they are using in their depo app. I just removed it. My suggestion is to not use it for now until those functions and APIs become stable. So I Removed it in my useServerResponse(...) function, which in here I renamed it to getServerResponse(...) because of the hook I created later in order to convert the promise into actual renderable response, so what I did was:
export async function getServerResponse(location) {
const key = JSON.stringify(location);
// const cache = unstable_getCacheForType(createResponseCache);
// let response = cache.get(key);
// if (response) return response;
let response = await createFromFetch(
fetch("/react?location=" + encodeURIComponent(key))
);
// cache.set(key, response);
return response;
}
and then creating a hook that would get the promise from the above function, and return an actual renderable result for me:
export function _useServerResponse(appState) {
const [tree, setTree] = useState(null);
useEffect(() => {
getServerResponse(appState).then((res) => {
setTree(res);
});
}, [appState]);
return { tree };
}
and finally in my AppContextProvider, I used that hook to get the react server component tree and use that rendered tree as child of my global context provider in client-side like below:
import { _useServerResponse } from ".../location/of/your/hook";
export default function AppContextProvider() {
const [appState, setAppState] = useState({
...someAppStateHere
});
const { tree } = _useServerResponse(appState);
return (
<AppContext.Provider value={{ appState, setAppState }}>
{tree}
</AppContext.Provider>
);
}
I know that this is like a workaround hacky solution, but it worked fine in my case, and seems like until we get stable APIs with proper official documentation about RSCs, it's a working solution for me at least!
I have recently started working on redux toolkit, Basically i want to refetch the data from database if there is mutation. I am using flatlist in my react native project where on pull to refresh i want my "getPosts" endpoint to refetch and get updated.
Note: I know about using tags for automated cache invalidation. but on web RTK query is not implemented.
Talking about pessimistic update:
async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) {
try {
const { data: updatedPost } = await queryF`enter code here`ulfilled
const patchResult = dispatch(
api.util.updateQueryData('getPost', id, (draft) => {
Object.assign(draft, updatedPost)
})
)
} catch {}
},
just give a help how to call this function from APP.JS or anywhere from the app. Thanks
You can just call refetch, you don't need anything that complicated. See the docs on useQuery
const myResult = useMyquery(args)
// in your pull to refresh trigger:
myResult.refetch()
If you are outside of a component, you can also still
store.dispatch(api.endpoints.myEndpoint.initiate(args, { track: false })))
to trigger a refetch
I am building an application with storefront, which uses nextJS. I am able to use getServerSide props while loading a new page.
The page contains many components, each needing their own data. At the moment, I am getting all of these into a results array and then returning from getServerSideProps, as shown below.
export async function getServerSideProps({query}) {
let sid = query.shop
let promises = []
promises.push(getShopCategories(sid))
promises.push(getShopInfo(sid))
promises.push(offersFromShop(sid))
try {
let allPromises = Promise.all(promises)
let results = await allPromises;
//console.log("Shop Info:", results[1])
return {props: {
id: sid,
shopCategories: results[0],
shopInfo: results[1],
offers4u: results[2].products
}}
} catch(e) {
console.error("Failure:", e)
return { props: {}}
}
}
But in this way, I have to get the data needed for all components in one shot. I was thinking, how to let each sub component in the page to have its own 'getServerSideProps'.
I can implement this in each component, but I am unsure about passing the parameters needed (such as shopId, productId etc., which I would have fetched in the main page).
One way is to use cookies, so that the server side can pick up these values. Is there a better solution?
getServerSideProps is only available on direct component under page folder
If you want to fetch more data on sub-component in the page, consider using the useEffect hook or componentDidMount for that work.
When testing the frontend of my Vue application, I sometimes want to skip the communication with the API backend.
I came up with the following idea:
add state in Vuex store: skipServerCommunication.
when skipServerCommunication = false: send the axios request as expected
when skipServerCommunication = true: return mocked data
The easy way would be to add an check before every call of axios.post(...) or axios.get(...), but I don't want to have to add code in every api function I wrote.
So I thought about using the interceptors of axios, but I think it's not possible to stop a request, and just return mocked data, right?
I then thought about wrapping axios, and dynamically returning the real axios instance or a mocked one, which also implements the .post(), .get() and all the other axios functions based on the Store state. I thought about something like this (I'm working with TypeScript):
import store from '#/vuex-store'
const axios = Axios.create( // real store config );
const axiosMock = {
get(path: string) {
if (path == '/test') {
return Promise.resolve( // mocked data );
}
return Promise.resolve(true)
}
}
class AxiosWrapper {
get instance() {
if (store.state.skipServerCommunication) {
return axiosMock;
}
return axios
}
}
export default new AxiosWrapper();
But this solution has some problems:
I need to replace all axios calls with axiosWrapper.instance.get(...). Can I somehow avoid this and mock axios in a way that I can still work with axios.get(...)?
VSCode isn't able to provide autocompletion anymore because the returned instance is either of type AxiosStatic or "MyMockType". So I thought about implementing the AxiosStatic interface, but I struggle to do that correctly because of the two anonymous functions in the AxiosInstance interface. Is there an alternative way to overcome this problem?
Use axios-mock-adapter instead. You can mock axios calls as needed per test, using advanced matching patterns.
Example usage:
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
describe('UserList', () => {
it('gets users on mount', async () => {
const mock = new MockAdapter(axios)
mock.onGet('/users').reply(200, {
users: [{ id: 1, name: 'John Smith' }],
})
const wrapper = shallowMount(UserList)
await wrapper.find('.getUsersBtn').trigger('click')
expect(wrapper.vm.users[0].id).toBe(1)
})
})
How can I override a value in the Apollo cache?
I have a graphql query to fetch the user. This returns a user with a default currency. This currency can then be override from a select dropdown.
The query fetches paymentCurrencies from an API, then uses a client side resolver to set the first item in the array of paymentCurrencies to be the users currency
query me {
me {
username
currency #client
paymentCurrencies
}
}
When somebody selects a currency from the dropdown menu, I want to over the users currency with whatever they have selected.
I have something like this so far:
const onChange = e => {
const { value } = e.target
client.writeData({ user: { currency: value, username, __typename: "User" } })
}
I get the following error: Error writing result to store for query:
{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GeneratedClientQuery"},"selectionSet":null}]}
Cannot read property 'selections' of null
Is using writeData is the correct method or should I be using writeQuery or something?
As described in the other answer you probably want a simple query and mutation setup. The client directive is used to extend your schema to hold client-only additional data. From your explanation, it sounds like you explicitly want this data to be syncronised with the server.
const ME_QUERY = gql`
query me {
me {
username
currency
paymentCurrencies
}
}
`;
const CURRENCY_MUTATION = gql`
mutation setCurrency($currency: String) {
setCurrency(currency: $currency) {
me {
username
currency
}
}
}
`;
function MyComponent() {
const { data } = useQuery(ME_QUERY);
const [setCurrency] = useMutation(CURRENCY_MUTATION);
const onChange = e => setCurrency({
variables: { currency: e.currentTarget.value },
});
return (
<>
<h2>{data && data.me.currency}</h2>
<select onChange={onChange}>
{/* your dropdown logic */}
</select>
</>
);
}
You get the idea. Apollo will now automatically update your cache. Make sure your mutation allows to query the updated user object.
For the automatic update to work your user needs to be recognised by the cache. You can do that by either adding an id field and selecting it in both the query and the mutation or implementing a dataIdFromObject function in Apollo Client 2.x that includes the username for __typename === 'User' or by using a type policy in Apollo Client 3.x. Find the docs here.
writeData should be used for changing fields at the root, for example:
{
yourState #client
}
In this case, you should use writeQuery. Additionally, this logic should really be extracted into a (local) mutation you can then call inside your component. When using writeQuery, the basic idea is to grab the existing data, make a copy and then transform it as needed:
const { me } = client.readQuery({ query: ME_QUERY })
const data = {
me: {
...me,
currency: value,
}
}
client.writeQuery({ query: ME_QUERY, data })
You can also use writeFragment to directly modify a single instance of an object in the cache. However, you need the object's cache key for this. Because the cache key is derived from the __typename and an id field, you should make sure the query includes an id field first. This is good practice regardless to ensure your cache can be updated easily (see here for more details). Then you can do something like this:
client.writeFragment({
id: 'User:42',
fragment: gql`
fragment UserCurrency on User {
currency #client
}
`,
data: {
currency: value,
},
})
It depends.
For persistent change (sync to server) you should just change user setting using mutation.
For session aware change - don't use user settings - copy this (from logged user properties) to separate value in global app state (redux/mobx or as in this case apollo local state).
In both cases rare problem can be with updating many components using changed data.
Redux/mobx solves this automatically.
Apollo HOCs won't rerender.
Hooks - updates partially (only the one with useQuery and useMutation pair), others will be updated only when rerendered.
Components build with <Query/> creates internally an observable then they will be updated, too.
Mutation have update and refetchQueries parameters for.
There is also a package for more complex use cases.