Relay modern caching example - react-native

I would like to enable caching in my react native application. I am using GraphQL with Relay modern. I found out that caching is not enabled by default in relay modern, but they have exposed RelayQueryResponseCache from relay-runtime, which we can add to the fetchQuery function in our API. I read discussion here and here about it, but have not seen any example to get started. Can someone help me out on this?
EDIT:
Ok I came up with a solution. I think it misses few things but so far it serves our needs.
I have noticed that passing anything into QueryRenderer into cacheConfig results passing that value into fetchQuery function inside my environment.
So I have created a Component which loads the data by some relation and resolves it into the correct json structure requested by the query. Then I return this into the state. Then I extended my Component which contains QueryRenderer with the created 'cache loader'. Now when componentWillMount() is called I ask for the cached data. During this I have set this.state.loading = true so I am able to handle loading state. Reading from DB is async.
I am using this class in other components as well. Every one handles its cache data. I just pass it to QueryRenderer.
However I was thinking that this makes some extra logic need to add for each Component which is supported by this caching. Probably passing the cache resolver as cacheConfig and resolve the cached data immediately inside the environment would be much more cleaner.

Related

Multiple requests from different components in Vue - best practice

My app consumes an self made - HTTP REST API. I use vueX for state management.
I have some nested components that needs access to the same http result as the parent, but I also use the component in other places where the parent does not fetch the result. Is there a way (pattern) to make sure a resource is not fetched multiple times.
I found this, and like the way of doing things, but I can't figure out how to make a new request to the server for updating the result. https://tkacz.pro/use-vuex-to-avoid-multiple-requests-from-different-components/
and is this even best practice ?
anyone has a better way ?
Using vuex and checking if your state is already populated is good enough. If it's not, then call a vuex action. It will help you to have a state shared globally in your app.
Not sure about this part
how to make a new request to the server for updating the result
What would be the issue or calling the API a second time? If needed, you can use the suggested solution in the comment, like adding a ?refresh or even and id to it id={randomId}.

Vue page cannot open after refreshing the page

I added the routes dynamically to the router and I can visit all pages with router-view. However, when I try to refresh the page I cannot visit the page again although the URL is the same. It is http://localhost:8081/me.
this.$router.addRoute("Main", {
path: '/me',
name: 'Me',
component: () => import(`#/components/Me.vue`)
});
As I said, I can normally visit the page "Me", but when I refresh it, I cannot see its content anymore with the warning:
[Vue Router warn]: No match found for location with path "/me"
I tried to create router with createWebHistory("/") after reading the cause of the error but nothing seems to change. I will appreciate any help.
There are two reasons why this would happen.
First serving SPA application from the server.
Make sure that your back-end is set to serve index.html file for all routes since back-end server is unaware of the routes set by client-side router. Something like express-history-api-fallback-middleware can help is using Node.js/Express.js.
Second problem is that of using addRoute.
As you described, the problem could be that Vue router is taking routing decision before your code in components/common/LeftMenu.vue is getting executed which is responsible for calling addRoute(). Ensure that this code is called before routing decision is being made. Best way would be to move this logic in top-level application component. And, if you can move that to top-level components, that means you can try to declare all routes while defining the router.
Why that should be done?
Using addRoute is not necessarily an anti-pattern but having to rely on addRoute is often code smell. You would really need it in extremely rare scenarios. Routing is a high-level architectural concern for your application and thus better to be declared upfront somewhere at top-level module even before application is getting initialized. Low level components should not attempt to manipulate routing data structure (violation of DIP principles).
One scenario where you might be tempted to use addRoute is taking decision after some data manipulation by the component with addition of some API call. That seems legitimate need but then to address that there are better ways. Considering using route guards (guards support async resolution too!) and prevent user from entering the view till the guard is resolved successfully.

Workbox/Vue: Create a custom variation on an existing caching strategy handler

Background:
I'm building an SPA (Single Page Application) PWA (Progressive Web App) using Vue.js. I've a remote PostgreSQL database, serving the tables over HTTP with PostgREST. I've a working Workbox Service Worker and IndexedDB, which hold a local copy of the database tables. I've also registered some routes in my service-worker.js; everything is fine this far....
I'm letting Workbox cache GET calls that return tables from the REST service. For example:
https://www.example.com/api/customers will return a json object of the customers.
workbox.routing.registerRoute('https://www.example.com/api/customers', workbox.strategies.staleWhileRevalidate())
At this point, I need Workbox to do the stale-while-revalidate pattern, but to:
Not use a cache, but instead return the local version of this table, which I have stored in IndexedDB. (the cache part)
Make the REST call, and update the local version, if it has changed. (the network part)
I'm almost certain that there is no configurable option for this in this workbox strategy. So I would write the code for this, which should be fairly simple. The retrieval of the cache is simply to return the contents of the requested table from IndexedDB. For the update part, I'm thinking to add a data revision number to compare against. And thus decide if I need to update the local database.
Anyway, we're now zooming in on the actual question:
Question:
Is this actually a good way to use Workbox Routes/Caching, or am I now misusing the technology because I use IndexedDB as the cache?
and
How can I make my own version of the StaleWhileRevalidate strategy? I would be happy to understand how to simply make a copy of the existing Workbox version and be able to import it and use it in my Vue.js Service Worker. From there I can make my own necessary code changes.
To make this question a bit easier to answer, these are the underlying subquestions:
First of all, the StaleWhileRevalidate.ts (see link below) is a .ts (TypeScript?) file. Can (should) I simply import this as a module? I propably can. but then I get errors:
When I to import my custom CustomStaleWhileRevalidate.ts in my main.js, I get errors on all of the current import statements because (of course) the workbox-core/_private/ directory doesn't exist.
How to approach this?
This is the current implementation on Github:
https://github.com/GoogleChrome/workbox/blob/master/packages/workbox-strategies/src/StaleWhileRevalidate.ts
I don't think using the built-in StaleWhileRevalidate strategy is the right approach here. It might be possible to do what you're describing using StaleWhileRevalidate along with a number of custom plugin callbacks to override the default behavior... but honestly, you'd end up changing so much via plugins that starting from scratch would make more sense.
What I'd recommend that you do instead is to write a custom handlerCallback function that implements exactly the logic you want, and returns a Response.
// Your full logic goes here.
async function myCustomHandler({event, request}) {
event.waitUntil((() => {
const idbStuff = ...;
const networkResponse = await fetch(...);
// Some IDB operation go here.
return finalResponse;
})());
}
workbox.routing.registerRoute(
'https://www.example.com/api/customers',
myCustomHandler
);
You could do this without Workbox as well, but if you're using Workbox to handle some of your unrelated caching needs, it's probably easiest to also register this logic via a Workbox route.

Request URI too long on spartacus services

I've been trying to make use of service.getNavigation() method, but apparently the Request URI is too long which causes this error:
Request-URI Too Long
The requested URL's length exceeds the capacity limit for this server.
Is there a spartacus config that can resolve this issue?
Or is this supposed to be handled in the cloud (ccv2) config?
Not sure which service are you talking about specifically and what data are you passing there. For starters, please read this: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414
Additionally it would benefit everyone if you could say something about the service you're using and the data you are trying to pass/get.
The navigation component is firing a request for all componentIds. If you have a navigation with a lot of (root?) elements, the maximum length of HTTP GET request might be too long for the given client or server.
The initial implementation of loading components was actually done by a POST request, but the impression was that we would not need to support requests with so many components. I guess we were wrong.
Luckily, the legacy POST based request is still in the code base, it's OccCmsComponentAdapter.findComponentsByIdsLegacy.
The easiest way for you to use this code, is to provide a CustomOccCmsComponentAdapter, that extends from OccCmsComponentAdapter. Then you can override the findComponentsByIds method and simply call the super.findComponentsByIdsLegacy and pass in a copy of the arguments.
A more cleaner way would be to override the CmsComponentConnector and directly delegate the load to the adapter.findComponentsByIdsLegacy. I would not start here, as it's more complicated. Do a POC with the first suggested approach.

How do you pass data to react components from express or koa without renderToString?

I'm unable to use React's server side rendering due to my use of client side libraries such as reqwest. I would like to pass some data to my react components, however. Is there a way to do this?
The easiest way to do this is by having api-client.js and api.js. In your browserify/webpack config you set up a client side version. For browserify put this in your package.json (feel free to edit and add webpack).
"browser": {
"./path/to/api.js": "path/to/api-client.js"
}
The second option is better in my opinion, but more difficult to implement. You create an abstract representation of your API that works like this:
var comments = require('./api').get('comments');
comments.getById('7').then(function(comment){ ... });
comments.create({...}).then(...);
On the server api.js simply calls the correct functions, which all return promises. On the client it returns a promise, makes an ajax request to the server, which calls these functions, and sends back the response, and the api client resolves/rejects its promise.
This allows the api to automatically work, and allows you to do additional things like track unfulfilled promises, and pre-populate state on the client, etc. (see react-async for example).